Быстрый старт
x состоит из CSS-утилит и набора маленьких JS-компонентов. Для обычной HTML-страницы достаточно подключить CSS, JS и вызвать x.init(). В демо-проекте это делает assets/js/app.js.
<link rel="stylesheet" href="dist/x.css" />
<script src="dist/x.js"></script>
<script>x.init()</script>
Если используешь исходники как ESM, импортируй публичный entrypoint:
import x from './index.js';
x.init();
x.init() инициализирует: Device, Modal, Animate, Appear, Lazyload, Loadmore, Sheets, Dropdown, Scroll, Sticky, Slider и Hover. Autocomplete и Typograf запускаются вручную, потому что им нужны пользовательские данные или явно выбранные селекторы.
Как устроен x
CSS
CSS собран из независимых файлов в src/components/x/*.css. Утилиты используют rem-шкалу: например .max70 означает max-width: 70rem, а не 700px.
JavaScript
Каждый компонент экспортирует singleton. src/js/x.js собирает их в объект x и кладёт его в window.x для браузерного использования.
Глобальные сокращения
| Сокращение | Что делает |
|---|---|
x.id(id) | Возвращает document.getElementById(id). |
x.qs(selector, context) | Возвращает первый элемент. Принимает селектор, NodeList, массив или Node. |
x.qsa(selector, context) | Возвращает массив элементов. Принимает селектор, NodeList, массив или Node. |
x.render(...) | Сокращение для x.lib.render(...). |
CSS
Breakpoints
Все responsive-утилиты mobile-first. Префикс добавляет правило внутри min-width.
| Префикс | Условие | Пример |
|---|---|---|
s: | min-width: 640px | s:flex |
m: | min-width: 768px | m:g2 |
l: | min-width: 1024px | l:px8 |
xl: | min-width: 1280px | xl:g4 |
Сетки и поток
.grid включает CSS Grid. .g1….g12 задают число колонок, .c1….c12 задают span элемента. .flow создаёт вертикальный поток, а .flow.s0….flow.s10 меняют расстояние между соседними элементами.
<div class="grid g1 m:g2 l:g4 gap4">
<div class="c1">Card</div>
<div class="c1 l:c2">Wide card</div>
</div>
Flex
.flex включает flex-контейнер. Направление: .fr, .fc. Перенос: .fw. Выравнивание: .aic, .ais, .aie, .jcc, .jcsb, .jcse, .jce.
Размеры
Все числовые width/max/min/height утилиты используют rem. Это основной принцип новой шкалы.
| Класс | Диапазон | Что задаёт |
|---|---|---|
.w* | 2-38 step 2, 40-160 step 10 | width |
.h* | 2-38 step 2, 40-80 step 10 | height |
.max* | 2-38 step 2, 40-160 step 10 | max-width |
.min* | 2-38 step 2, 40-160 step 10 | min-width |
Примеры: .max70 = 70rem, .w16 = 16rem, .h40 = 40rem.
Lib utilities
В CSS-разделе Lib собраны классы общего назначения: .container, .unselectable, .unclickable, .pointer, .toe, .lc1….lc5, .oh, .fix, .rel, .abs, .inset, .fill, .hidden, .hidden-next, .visuallyHidden, .scroll-responsive.
Aspect ratio: .ratio1x1, .ratio2x1, .ratio3x1, .ratio4x1, .ratio3x2, .ratio4x3, .ratio16x9, .ratio1x2, .ratio1x3, .ratio1x4, .ratio2x3, .ratio3x4, .ratio9x16.
Spacing, radius, shadow, order
Spacing построен на переменных --space-0…--space-10, но классы margin/padding/gap идут до 12. Для отрицательных margin используется суффикс n: .mt2n.
Радиусы: .br0….br12, .br100. Тени: .sh0….sh5. Порядок flex/grid элемента: .o0….o10.
Типографика и цвета
Размеры шрифта: .fs10….fs19 step 1 и .fs20….fs100 step 2. Вес: .fw100….fw900. Также есть .mono, .italic, .strike, .ls0….ls4, .lh0….lh9.
Цвета: .color-primary, .color-success, .color-warning, .color-danger, .color-error, .color-black, .color-white, .color-grey. Для фона используются соответствующие .bg-*.
JS-компоненты
Animate
x-animate привязывает элемент к прогрессу скролла. Компонент может вызывать глобальную функцию, добавлять класс, убирать класс после выхода из диапазона и работать с кастомным trigger.
| Параметр JSON | Описание |
|---|---|
trigger | Селектор элемента, относительно которого считается прогресс. Если не указан, используется сам элемент. |
start, end | Границы диапазона. Поддерживаются px, vh, проценты и числовые значения. |
functionName | Имя глобальной функции. Она получает объект с прогрессом и состоянием. |
class | Класс, который добавляется при попадании в диапазон. |
classRemove | Если true, класс снимается после выхода из диапазона. |
<div x-animate='{"start":"80vh","end":"20vh","functionName":"onProgress","class":"active"}'></div>
<script>function onProgress(data) { console.log(data.progress); }</script>
Appear
x-appear добавляет .appeared при первом появлении и переключает .visible, пока элемент виден. Подходит для простых reveal-анимаций.
API: x.appear.init({ once, rootMargin, threshold }), x.appear.destroy().
Autocomplete
Autocomplete работает поверх Dropdown: нужен контейнер с id, поле x-dropdown-open и список x-dropdown. Данные можно грузить асинхронно, фильтровать через mapData, рендерить через renderItem.
<div id="searchDropdown">
<input x-dropdown-open type="text" placeholder="Search" />
<ul x-dropdown></ul>
</div>
x.autocomplete.init('searchDropdown', {
loadData: async (ctx) => fetch('/api?q=' + encodeURIComponent(ctx.field.value)).then(r => r.json()),
renderItem: item => `<li data-item='${JSON.stringify(item)}'>${item.name}</li>`,
onSelect: item => x.form.setValue('#searchDropdown input', item.name)
});
API: x.autocomplete.init(dropdownId, options), x.autocomplete.destroy(dropdownId), x.autocomplete.destroy(), x.autocomplete.get(dropdownId).
Device
x.device определяет breakpoint, touch, browser, OS и добавляет классы на html. На resize состояние пересчитывается. Это удобно для CSS-ветвлений и диагностики.
API: x.device.init(), x.device.destroy().
Dropdown
Dropdown связывает trigger x-dropdown-open и соседний список x-dropdown. Поддерживает клики вне меню, keyboard navigation, ARIA-роли и события жизненного цикла.
| Событие | Когда вызывается |
|---|---|
dropdown:beforeshow | Перед открытием. |
dropdown:aftershow | После открытия. |
dropdown:beforehide | Перед закрытием. |
dropdown:afterhide | После закрытия. |
API: x.dropdown.init(), x.dropdown.closeAllDropdowns(), x.dropdown.destroy().
Hover
x-hover синхронизирует класс .hover у ссылок с одинаковым href. Наиболее полезно, когда карточка и текстовая ссылка ведут в одно место.
API: x.hover.init(), x.hover.destroy().
Lazyload
x-lazyload наблюдает изображения через IntersectionObserver, переносит data-src/data-srcset в реальные атрибуты и добавляет .loaded. Если данных для загрузки нет, элемент просто снимается с наблюдения.
<img x-lazyload src="placeholder.png" data-src="photo.jpg" data-srcset="photo@2x.jpg 2x" alt="" />
API: x.lazyload.init(), x.lazyload.destroy().
Loadmore
x-loadmore вызывает глобальную функцию при приближении маркера к viewport. Функция получает номер страницы и должна вернуть truthy, если можно продолжать загрузку, или falsy, если данных больше нет.
<div x-loadmore='{"functionName":"loadPosts"}'></div>
window.loadPosts = async (page) => {
const ok = await fetchPage(page);
return ok;
};
API: x.loadmore.init(), x.loadmore.reset(selector), x.loadmore.destroy().
Modal
Modal открывается через x-modal-open="id", а содержимое задаётся блоком x-modal="id". При инициализации содержимое переносится в modal-контейнер. Поддерживаются hash-модалки, уникальные модалки, вложенные окна, overlay click и Esc.
<a x-modal-open="profile">Open</a>
<div x-modal="profile" class="modal_hash" data-window-class="max80">...</div>
| API | Описание |
|---|---|
x.modal.show(id) | Открыть модалку. |
x.modal.hide(id) | Закрыть модалку. |
x.modal.hideAll() | Закрыть все активные модалки. |
x.modal.isActive(id) | Проверить активность. |
x.modal.destroy() | Снять глобальные обработчики. |
События: modal:ready, modal:open, modal:close.
Scroll
x-scrollto скроллит к id, selector или JSON-конфигу. Для модалок можно указать внутренний scroll-parent.
<a x-scrollto="#top">Наверх</a>
<a x-scrollto='{"parent":"#modal .modal-outer","target":"#api","offset":100}'>API</a>
API: x.scroll.init(), x.scroll.destroy().
Sheets
Sheets - это табы. Контейнер помечается x-sheets, кнопки - x-sheet-open, панели - x-sheet. Активная кнопка получает .active, активная панель показывается.
<div x-sheets>
<a x-sheet-open="a" class="active">A</a>
<a x-sheet-open="b">B</a>
<div x-sheet="a">Panel A</div>
<div x-sheet="b">Panel B</div>
</div>
API: x.sheets.init(), x.sheets.show(xSheet, options), x.sheets.destroy(). События: sheets:beforeshow, sheets:aftershow.
Slider
x-slider подготавливает слайды, lazy-загружает изображения, поддерживает touch-drag на мобильных и hover-preview на desktop. Конфиг передаётся JSON-атрибутом.
Частые параметры: gap, touch, hover, rubber, indicators, resetOnMouseout.
API: x.slider.init(), x.slider.destroy(), x.slider.observeSliders().
Sticky
x.sticky наблюдает элементы .sticky. Когда элемент начинает “прилипать”, добавляется .sticky_on и отправляется событие sticky:on. При возврате отправляется sticky:off.
API: x.sticky.init(), x.sticky.reset(), x.sticky.destroy().
Typograf
x.typograf.process(html) обрабатывает кавычки, многоточия, дроби, степени, тире, минус в числах и неразрывные пробелы. Теги code, pre, script, style пропускаются.
x.typograf.live(staticSelector, interactiveSelector, root) обрабатывает статичный текст и вешает live-обработку на input, textarea и contenteditable.
x.form
x.form нужен, чтобы программно менять поля и при этом запускать правильные события. Он работает с input, textarea, select, checkbox/radio и contenteditable.
| Метод | Описание |
|---|---|
x.form.setValue(selector, value) | Записывает value и dispatch-ит подходящее событие. |
x.form.setChecked(selector, checked) | Меняет checked у checkbox/radio и dispatch-ит input. |
x.form.onUpdate(selector, callback) | Подписывает callback на input/change в зависимости от типа поля. |
x.form.offUpdate(selector) | Снимает подписку с выбранных элементов. |
x.form.update(selector) | Принудительно dispatch-ит update-событие. |
x.form.destroy() | Снимает все подписки, созданные через form. |
x.form.getEventType(element) | Возвращает событие, которое будет использоваться для элемента. |
x.lib
DOM и классы
x.lib.id, x.lib.qs, x.lib.qsa, x.lib.hide, x.lib.show, x.lib.toggle, x.lib.switch, x.lib.addClass, x.lib.removeClass, x.lib.toggleClass, x.lib.switchClass.
Навигация
x.lib.reload(), x.lib.reloadWithHash(hash), x.lib.redirectTo(url), x.lib.updateURL(url, title).
Форматирование и валидация
x.lib.random(a, b), x.lib.price(price), x.lib.number(num), x.lib.numberDecline(number, one, few, many), x.lib.phoneCleaner(phone), x.lib.phone(phone), x.lib.isEmail(email), x.lib.isValidJSON(str), x.lib.isInteger(value), x.lib.isDouble(value), x.lib.makeId(), x.lib.makePassword(length, selector).
x.lib.phoneCleaner(phone) оставляет только цифры, приводит российский префикс 8 к 7 (кроме номеров 8 800), добавляет 7, если код страны отсутствует, и возвращает не более 11 цифр. x.lib.phone(phone) дополнительно форматирует полный номер.
x.lib.phoneCleaner('8 (912) 345-67-89'); // '79123456789'
x.lib.phone('8 (912) 345-67-89'); // '+7 912 345-67-89'
x.lib.phone('8 800 555-35-35'); // '8 800 555-35-35'
Runtime
x.lib.loadScript(path, callback, type) грузит внешний скрипт один раз. x.lib.deferred(callback, delay) запускает код после пользовательской активности или таймера. x.lib.deffered сохранён как алиас с исторической опечаткой.
x.lib.throttle(fn, wait, options) и x.lib.debounce(fn, wait, options) возвращают функцию с методом cancel(). Options: leading, trailing.
x.lib.onAppear(selector, appearCallback, disappearCallback, options) создаёт IntersectionObserver. Если disappearCallback не передан, элемент снимается с наблюдения после первого появления.
Render
x.lib.render(selector, data, placement = null, escapeHtml = false) рендерит строку, функцию или async-функцию. Если placement не указан, заменяет innerHTML. Если включён escapeHtml, вставляет безопасный текст.
x.lib.render('.out', '<b>HTML</b>');
x.lib.render('.out', userInput, null, true);
x.lib.render('.list', () => '<li>Item</li>', 'beforeend');
Жизненный цикл
Большинство компонентов можно безопасно переинициализировать: init() либо сам снимает старые обработчики, либо пропускает уже обработанные элементы. Для SPA лучше вызывать destroy() при размонтировании страницы или перед полной заменой DOM.
| Когда | Что делать |
|---|---|
| Обычная статичная страница | Один раз вызвать x.init(). |
| После AJAX-вставки HTML | Повторно вызвать нужный компонент или x.init(), если менялась большая часть страницы. |
| SPA route leave | Вызвать destroy() у компонентов с глобальными обработчиками. |
Безопасность и ограничения
x-render использует JavaScript expression из HTML-атрибута. Используй его только в доверенной разметке. Для пользовательского текста предпочитай x.lib.render(selector, userInput, null, true).
x-loadmore и x-animate вызывают функции по имени из глобальной области. Не передавай туда непроверенные имена функций из пользовательских данных.
Lazyload и Autocomplete зависят от браузерных API и внешних ресурсов. Для локального открытия через file:// лучше использовать обычный script bundle и локальные placeholder-изображения, как в index.html.