Контракт оформления
Каждая публичная часть напрямую принимает ваши className, style, ref, обработчики событий, нативные свойства и атрибуты data-*. Параллельных свойств вроде contentClassName или triggerClassName нет.
Атрибуты идентичности и состояния принадлежат uiify. Они служат точками привязки стилей: меняйте состояние через свойства или нативное поведение HTML, а не записью в data-state. Пространство имён --uiify-* зарезервировано для геометрии, анимации и значений отдельных экземпляров.
<Popover.Root>
<Popover.Trigger className="toolbar-trigger">Open</Popover.Trigger>
<Popover.Content className="toolbar-popover" data-layout="compact">
Actions
</Popover.Content>
</Popover.Root>Именование
| Точка привязки | Назначение | Пример |
|---|---|---|
| Маркеры идентичности | Стабильная идентичность компонента, задаётся uiify | data-uiify-popover |
| Маркеры анатомии | Публичная часть составного компонента, задаётся uiify | data-part="content" |
| Атрибуты состояния | Доступное только для чтения отражение состояния для селекторов | data-state="open" |
| Пользовательские свойства | Динамическая геометрия и значения отдельных экземпляров | --uiify-anchor-x |
У каждого корня с состоянием есть один маркер data-uiify-<component>, значимо само его наличие. У каждой составной части есть одно значение data-part в kebab-case, например trigger, content, item или label. Свойства потребителя не могут переопределить эти значения.
Обычный CSS и Tailwind
[data-uiify-popover][data-part="content"] {
min-inline-size: 12rem;
}
[data-uiify-popover][data-part="content"][data-state="open"] {
opacity: 1;
}
/* Tailwind uses the same attributes. */
<div className="data-[state=open]:opacity-100" />Полиморфизм
as и render заменяют отображаемый элемент. Собственная функция рендеринга должна передавать полученные свойства и ref в DOM-элемент, иначе маркеры, состояние и оформление перестанут работать.
behavior.css обязательна
Эта таблица стилей содержит правила, обеспечивающие работу состояния: позиционирование всплывающих блоков, размеры ::details-content, распространение через :has(), размещение скрытых полей ввода и переходы @starting-style. В ней нет цветов, шрифтов и отступов.
import "@gunkin/uiify/components/behavior.css";Без неё компоненты отображаются, но их состояние не работает. Импортируйте её один раз в любом месте приложения.
Состояние задаётся атрибутом, а не классом
Компоненты отражают состояние в атрибутах data-* элемента, которому оно принадлежит, поэтому CSS может выбирать его без JavaScript и имён классов. На странице каждого компонента перечислены создаваемые им атрибуты.
.my-trigger[data-state="open"] { … }
.my-item[data-disabled] { … }
/* Native state works the same way, because the state IS native */
.my-panel:popover-open { … }
details[open] > summary { … }Варианты тоже задаются атрибутами data-*, а не запросами стилей контейнера: контейнер не может запрашивать самого себя.
Значения — в разметке, правила — в таблице стилей
Значения отдельных экземпляров передаются как встроенные пользовательские свойства; все правила находятся в статической таблице стилей. Встроенные стили не поддерживают псевдоклассы, псевдоэлементы, запросы контейнера и @starting-style, а модель состояния зависит от всех этих механизмов.
Встроенные свойства передаются потоком вместе с HTML без расхождений при гидратации, а единственная таблица стилей разбирается один раз и кешируется. CSS-in-JS вместо этого вставляет блоки <style> в каждую границу и не кеширует их между запросами.
Три способа оформления
Собственный CSS. Выбирайте элемент и его атрибуты data-*; переопределять стили по умолчанию не нужно — их нет.
Tailwind. Те же атрибуты работают как варианты, например data-[state=open]:rotate-180. Примеры на сайте переключаются на оформление Tailwind в настройке Style в шапке.
@gunkin/uiify/styles. Необязательный слой обычного CSS со шкалами токенов OKLCH, светлой и тёмной темами и оформлением для каждого компонента — отправная точка, а не требование.
import "@gunkin/uiify/styles/reset";
import "@gunkin/uiify/styles";Порядок каскада
Всё, что поставляет uiify, находится в именованном слое ниже вашего:
@layer ui.reset, ui.behavior, ui.tokens, ui.theme, app;Ваш слой всегда имеет приоритет: для переопределений не нужны !important и ухищрения со специфичностью, а рефакторинг библиотеки их не ломает.
На каждой странице компонента перечислены его атрибуты и CSS-переменные, а также готовый к копированию CSS для обоих вариантов оформления.