Мне нравится идея создания компонентов без JS. Видео Native CSS Components vs Modern UI Libraries показывает реализацию нескольких компонентов на HTML/CSS и сравнивает их с аналогами из Shadcn.
Компоненты в Shadcn действительно переусложнены. Но у показанных в видео аналогов есть проблемы. Рассмотрим, что не так с каждой реализацией и как можно сделать лучше (сейчас или в будущем).
Диалог
Диалог реализован с помощью Popover API.
popover не задаёт нужную семантику. <div popover> остаётся контейнером с role="group". У диалога должна быть соответствующая семантика, состояния, поведение и UX.Есть
<dialog>, который обладает нужной семантикой и свойствами. Он работает с popover для немодальных диалогов, а с command для модальных и немодальных. Атрибут closedby="any" добавляет закрытие по нажатию вне диалога.<!-- немодальный диалог с popover -->
<button
popovertarget="dlg"
popovertargetaction="show"
>
Open dialog
</button>
<dialog id="dlg" popover>
...
<button
popovertarget="dlg"
popovertargetaction="hide"
>
Close
</button>
</dialog>
<!-- немодальный диалог с command -->
<button
commandfor="dlg"
command="show-popover"
>
Open dialog
</button>
<dialog id="dlg" popover>
...
<button
commandfor="dlg"
command="hide-popover"
>
Close
</button>
</dialog>
<!-- модальный диалог с command и closedby -->
<button
commandfor="dlg"
command="show-modal"
>
Open dialog
</button>
<dialog id="dlg" closedby="any">
...
<button
commandfor="dlg"
command="close"
>
Close
</button>
</dialog>
Выпадающее меню
В видео выпадающее меню реализовано с помощью
popover и Anchor Positioning. Как и с диалогом, меню не обладает нужной семантикой и поведением. Dropdown menu в Shadcn реализован как элемент с ролью menu.Виджет
menu предназначен для команд, а не навигационных ссылок. Внутри допустимы только определённые роли. Требуется навигация стрелками и Home/End. Для этого нужен JS. Всё может измениться с приходом focusgroup, но пока это экспериментальный API.<button
commandfor="menu"
command="toggle-popover"
>
Open menu
</button>
<div
id="menu"
role="menu"
popover
focusgroup="menu"
>
<button role="menuitem">
Profile
</button>
<button role="menuitem">
Settings
</button>
<button role="menuitem">
Sign out
</button>
</div>
Всплывающая подсказка
В видео отображение подсказки реализовано через
:hover/:focus и комбинатор ~. Роль подсказки — tooltip, а кнопка ссылается на неё через aria-describedby. Такая всплывающая подсказка нарушает критерий WCAG 1.4.13 Content on Hover or Focus. Реализация поведения для соответствия 1.4.13 требует JS. Можно использовать
popover с его закрытием по Esc, но отображение потребует JS. Interest Invokers API и popover="hint" позволят в будущем реализовать это без JS.<button
interestfor="copy"
aria-describedby="copy"
>
Copy
</button>
<span
id="copy"
popover="hint"
role="tooltip"
>
Hover or focus me
</span>
Вкладки
Вкладки реализованы как набор радио-кнопок, которые в сочетании с псевдо-классом
:checked и селектором :has() у родителя переключают дочерние панели. Вновь не хватает нужной семантики и поведения и в целом это хак.На канале есть разбор вкладок с использованием ссылок
<a> и псевдо-класса :target и почему это не вкладки. То же с радио-кнопоками и :has(). Реализовать вкладки без JS сейчас нельзя, но, возможно, появятся новые элементы или примитивы.Аккордеон
На последок виджет аккордеона. В видео он сделан из нескольких
<details> и <summary>. Также демонстрируется новый псевдо-элемент ::details-content и свойство interpolate-size: allow-keywords для анимации раскрытия.Вот аккордеон можно использовать вместо решений на JS, это не хак. Тут ещё можно углубиться в различия аккордеона и раскрываемых панелей, но в другой раз. А примеры диалога, меню, подсказки и вкладок из видео использовать не стоит.
#html #css #js #ui