Перейти к содержимому
EN
Масштаб
Плотность
Тема
Акцент

Действия

Чип

JS Переключение, стрелки и бегущий tabindex — делает `instrument.js`

Фильтр, который видно и который включают нажатием. Включённых бывает несколько сразу — и в этом отличие от сегментированного контрола, где выбранное значение всегда ровно одно.

Компонент
идёт упало в очереди готово отменено
Код
<div class="inst-chips" role="listbox" aria-multiselectable="true"
     aria-orientation="horizontal" aria-label="Фильтр по состоянию">
  <span class="inst-chip" role="option" aria-selected="true" tabindex="0">идёт</span>
  <span class="inst-chip" role="option" aria-selected="true" tabindex="-1">упало</span>
  <span class="inst-chip" role="option" aria-selected="false" tabindex="-1">в очереди</span>
  <span class="inst-chip" role="option" aria-selected="false" tabindex="-1">готово</span>
  <span class="inst-chip" role="option" aria-selected="false" aria-disabled="true" tabindex="-1">отменено</span>
</div>

Контракт

Что Обязательно Почему
role="listbox" на полосе да Без него role="option" невалиден, а стрелки не заработают
aria-label на полосе да «Фильтр по состоянию»: иначе скринридер объявит список без имени
aria-selected на каждом чипе да Носитель выбора. false тоже пишется — отсутствие атрибута значит «выбор здесь не предусмотрен»
tabindex да, начальный Ноль у одного, -1 у остальных. Дальше его ведёт instrument.js. Без бегущего tabindex Tab пройдёт по каждому чипу и полоса перестанет быть одним контролом
aria-orientation="horizontal" да Полоса идёт вбок, а listbox по умолчанию вертикальный: без атрибута перебор повесился бы на ↑ ↓ при ряде, идущем вправо
aria-multiselectable="true" нет Ставится, когда включённых бывает несколько. Без него полоса ведёт себя как одиночный список: выбор идёт за фокусом

Клавиатура: Tab — вход в полосу и выход из неё, ← → — перебор, пробел — включить или выключить.

Выбранный чип несёт два признака, а не один цвет: цвет подписи и заливку. Наведение занимает третий канал — рамку — и потому не спорит с выбором. Подпись невыбранного --text-secondary, выбранного --accent-text, обе проверены на 4.5:1 в пяти темах и четырёх акцентах. Цель нажатия — высота --control-h-sm с зазором полосы --space-2, проверено cmd/targets во всех пятнадцати сочетаниях масштаба и плотности.

Событие inst:select приходит с detail.selected — включили или выключили. Отменённое событие оставляет разметку нетронутой: приложение взяло состояние на себя и поставит aria-selected само.

Состояния

Чип не имеет тона. Он называет фильтр, а не состояние: покрасив «упало» в красный, мы сказали бы, что сам фильтр аварийный. Цвет в чипе означает ровно одно — включён он или нет.

Три канала, и они не пересекаются, поэтому читаются вместе:

Канал Что кодирует
Цвет подписи выбран — --accent-text, нет — --text-secondary
Цвет рамки под курсором — --accent-border, иначе — --border
Заливка есть, если выбран или под курсором

Начертание при выборе не меняется намеренно: полоса переносится по ширине, и потолстевшая подпись переложила бы соседей на другую строку в момент нажатия.

JS

Модуль подключается один раз на страницу — инициализировать компоненты по отдельности не нужно.

Что делает instrument.js

Примеры на этой странице живые: войдите в полосу Tab, дальше ← и →, включение — пробелом. Мышью тоже.

Полоса объявлена как role="listbox", и instrument.js выполняет её контракт: ← → между чипами, Home и End, один Tab на всю полосу.

Одиночный и множественный

Атрибут aria-multiselectable меняет не только объявление, но и поведение клавиатуры, и это требование APG, а не решение библиотеки.

стрелка пробел
Без атрибута двигает фокус и выбор ничего нового
aria-multiselectable="true" двигает только фокус включает и выключает

Иначе пройти полосу стрелками значило бы включить всё, мимо чего прошёл.

События

inst:select всплывает с переключённого чипа. В detail.selected — стало включено или выключено; код — на вкладке JS у примера в шапке.

Опции

Атрибут Что делает
data-value Значение в detail вместо подписи чипа
aria-orientation="vertical" Стрелки по вертикали. Для полосы, поставленной столбиком

API

Классы
ИмяЧто делает
класс
inst-chipsПолоса. Несёт role="listbox" и переносится по ширине
inst-chipОдин фильтр. Несёт role="option"
Атрибуты
ИмяЗначениеЧто делает
атрибут
aria-orientationhorizontalНа .inst-chips. Обязателен: полоса идёт вбок, а listbox по умолчанию вертикальный
aria-multiselectabletrueНа .inst-chips. Включает несколько сразу: стрелка тогда двигает только фокус, а включает пробел
aria-selected—true · false на каждом чипе. Носитель выбора
data-value—На .inst-chip. Что придёт в detail события inst:select вместо текста
aria-disabledtrueФильтр недоступен: например, под него нет ни одной строки
Токены
ИмяЗначениеОткуда
токен
--control-h-sm24pxбаза +14: плотность · масштаб
--radius-sm6pxбаза +4: масштаб
--text-xs0.78125remбаза +4: масштаб
--space-24pxодно на все ячейки
--space-36pxодно на все ячейки
--surface-recessedlight-dark(oklch(0 0 0 / 0.060), oklch(1 0 0 / 0.050))одно на все ячейки
--accent-textlight-dark(var(--a-5), var(--a-3))база +1: тема
--accent-borderlight-dark(var(--a-4), var(--a-3))одно на все ячейки
--borderlight-dark(oklch(0 0 0 / 0.12), oklch(1 0 0 / 0.11))одно на все ячейки
--hairline1pxбаза +1: экран

Почему кит устроен именно так — конституция · Открытый код под MIT, github.com/keshon/instrument