Multiselect
Multiselect позволяет выбрать несколько пунктов из списка. Выбранные пункты видны как удаляемые чипы и все отправляются вместе с формой.
<tk-field label="Colors">
<tk-multiselect name="colors" placeholder="Choose colors">
<option value="red" selected>Red</option>
<option value="green">Green</option>
<option value="blue" selected>Blue</option>
<option value="yellow">Yellow</option>
</tk-multiselect>
</tk-field>Поместите multiselect в aria-label, иначе у поля не будет имени вовсе.
Анатомия
| Часть | Что это |
|---|---|
tk-multiselect |
сам элемент; одна остановка Tab |
| поле | содержит чипы, а с searchable ещё и строку поиска; растёт, когда чипы переносятся |
| чип | один выбранный пункт, удаляемый |
| кнопка «очистить всё» | видна, когда что-то выбрано; убирает все чипы |
| шеврон | переворачивается, пока меню открыто |
| меню | список пунктов; ширина как у поля; не закрывается, пока человек выбирает |
| пункт | одна строка с чекбоксом в начале |
Варианты
<tk-multiselect variant="filter" filter-label="Status"> <option value="open" selected>Open</option> <option value="review" selected>In review</option> <option value="closed">Closed</option> </tk-multiselect>
field(по умолчанию): поле формы с чипами. Имя дайте через Field илиaria-label.filter: компактная кнопка для панели фильтров. Пока ничего не выбрано, на ней написано «Status», потом «Status (2)», и у неё появляется заливка. Выбор живёт в меню, чипов нет.filter-labelобязателен: это и видимый текст, и доступное имя. Не помещайте фильтр в Field.
Поиск
<tk-multiselect searchable aria-label="Skills" placeholder="Search skills"> <option>Accessibility</option> <option>Illustration</option> <option>Motion design</option> <option>Prototyping</option> </tk-multiselect>
searchable добавляет текстовое поле, которое фильтрует меню по мере ввода, как в
loadOptions включает его сам. Вариант filter поиска не поддерживает.
Размеры
<tk-multiselect aria-label="Days" placeholder="Choose days"> <option>Monday</option> <option>Tuesday</option> </tk-multiselect> <tk-multiselect size="large" aria-label="Months" placeholder="Choose months"> <option>January</option> <option>February</option> </tk-multiselect>
size |
Мин. высота | Padding-inline | Текст | Строка пункта |
|---|---|---|---|---|
medium (по умолчанию) |
32px | space-8 |
Body Small (14/20) | 32px |
large |
36px | space-10 |
Body Medium (16/24) | 36px |
Когда в поле есть чипы, отступ слева сокращается до space-4, чтобы чипы выровнялись
по краю. Чипы переносятся, и поле растёт; max-rows ограничивает его примерно этим
числом строк, остальное прокручивается.
Состояния
<tk-multiselect aria-label="Regions" disabled> <option selected>Europe</option> </tk-multiselect> <tk-multiselect aria-label="Languages" readonly> <option selected>English</option> <option selected>Dutch</option> </tk-multiselect>
При наведении поле не меняется. При фокусе с клавиатуры его рамка меняет цвет и
становится толще; отдельной рамки фокуса снаружи нет. Когда фокус на чипе, рамку фокуса
показывает чип. Вариант filter при наведении тонируется и при фокусе показывает рамку
фокуса.
disabled: на поле нельзя перейти, меню не открывается, значения не отправляются. Чипы удалить нельзя.readonly: чипы видны и отправляются. На поле можно перейти, но меню не открывается, чипы не удаляются, кнопка «очистить всё» и шеврон скрыты.- Ошибка: задайте
errorна Field. Сообщение появится ниже, а рамка станет красной, даже в фокусе. Вариантfilterкрасным не становится.
Ограничение выбора
<tk-field label="Topics" helper="Choose up to 2">
<tk-multiselect max="2" placeholder="Choose topics">
<option>Design</option>
<option>Research</option>
<option>Writing</option>
</tk-multiselect>
</tk-field>max ограничивает число выбранных пунктов. Когда предел достигнут, остальные пункты
отключаются, а меню пишет «You can choose up to 2.» Выбранные пункты по-прежнему можно
убрать.
Пункты и данные
- Пункты пишутся дочерними
<option>, группы — через<optgroup>.selectedзадаёт начальный выбор. data-descriptionдобавляет в меню короткую вторую строку; на чипе видно только название.- Пункты можно передать и через свойство
optionsили загрузчикloadOptions, как вCombobox . С загрузчиком строка поиска есть всегда. creatableдобавляет строкуCreate "…", когда поиск ничего не нашёл; её выбор добавляет новый чип. Работает поправилам Combobox и останавливается наmax.
Когда использовать
- Multiselect нужен, когда можно выбрать несколько пунктов из списка, который слишком длинный, чтобы показать его целиком.
- Для короткого набора, который помещается на экране, берите
Checkbox group . Она проще и показывает все пункты. - Если выбирают ровно один пункт, нужен
Select илиCombobox .
Текст
- Подпись: назовите, что вы спрашиваете, с заглавной буквы только в первом слове, без двоеточия.
- Placeholder: «Choose…». Он пропадает, когда что-то выбрано, поэтому не пишите в нём ничего нужного.
- Пункты: каждый в одну короткую строку, в порядке, который легко угадать. Ставьте первым слово, по которому пункты различаются. Не начинайте их с иконки или эмодзи.
- Предел: если задан
max, скажите об этом в подсказке: «Choose up to 3». - Ошибка: скажите, как её исправить, словами подписи: «Choose at least one topic».
Подробнее в
Можно / нельзя
| ✅ Можно | ❌ Нельзя |
|---|---|
| Брать группу чекбоксов для короткого набора | Прятать несколько пунктов в multiselect |
Добавлять searchable для длинных списков |
Заставлять листать длинный список в поисках пункта |
Задавать max и называть его в подсказке |
Молча отбрасывать лишний выбор |
| Давать подпись через Field | Использовать placeholder вместо подписи |
| Ставить первым слово, по которому пункты различаются | Начинать пункты с иконок или эмодзи |
Связанные
Field : подпись, подсказка и ошибка вокруг multiselect.Checkbox group : короткий набор, всё на виду.Select : один пункт из короткого списка; у него такой же вариантfilter.Combobox : один пункт из длинного списка, с той же фильтрацией.Tag : чип, из которого собран multiselect.Select family : как они соотносятся.
API
Атрибуты
| Атрибут | Тип | По умолчанию | Что делает |
|---|---|---|---|
placeholder |
string | — | подсказка, пока ничего не выбрано |
name |
string | — | имя поля в форме; под ним отправляется каждое выбранное значение |
required |
boolean | false |
пустой выбор не даёт отправить форму; видимой метки нет (звёздочку добавляет Field) |
disabled |
boolean | false |
нельзя перейти и открыть, не отправляется с формой |
readonly |
boolean | false |
можно перейти, значения отправляются, но открыть и изменить нельзя |
size |
medium | large |
medium |
высота 32 / 36px |
variant |
field | filter |
field |
поле формы или кнопка панели фильтров |
filter-label |
string | — | обязателен с variant="filter": текст кнопки и её доступное имя |
searchable |
boolean | false |
добавляет строку поиска, которая фильтрует меню |
creatable |
boolean | false |
позволяет добавить пункт, которого нет в списке |
max |
number | — | сколько пунктов можно выбрать максимум |
max-rows |
number | — | сколько строк чипов видно, прежде чем поле начнёт прокручиваться |
min-chars |
number | 0 |
с loadOptions: сколько символов нужно до загрузки |
debounce |
number | 250 |
с loadOptions: сколько миллисекунд ждать после ввода |
Свойства
| Свойство | Тип | Что делает |
|---|---|---|
value |
string[] |
выбранные значения в порядке выбора |
options |
{ value, label, description?, disabled?, group? }[] |
пункты из JavaScript; заменяют дочерние <option> |
loadOptions |
(query, { signal }) => Promise<options> |
загружает пункты под запрос; включает строку поиска |
Слоты
| Слот | Содержимое |
|---|---|
| по умолчанию | элементы <option> и <optgroup>; их изменения подхватываются |
События
change срабатывает, когда человек отмечает или снимает пункт, удаляет чип, очищает всё
или создаёт пункт. Оно не срабатывает, когда вы задаёте value из кода. Новое значение
читайте из value.
В форме каждое выбранное значение отправляется под именем name, как у группы чекбоксов,
а сброс формы возвращает начальный выбор.
CSS
::part(field): поле или кнопка в вариантеfilter.::part(listbox): меню.
Токены
Поле, меню, пункты, ошибка и фокус используют
| Часть | Токены |
|---|---|
| чекбокс пункта | surface/default + border/subtle, radius-4; в подсвеченной строке border/strong |
| чекбокс пункта, отмечен | fill/accent + галочка icon/accent/on-fill; в подсвеченной строке fill/accent/hover |
| чекбокс пункта, отключён | surface/disabled + border/disabled, галочка icon/disabled |
| filter, наведение | state/hover |
| filter, что-то выбрано | state/on + border/selected (state/on-hover под курсором) |
Доступность
Уже встроено:
- Скринридер объявляет combobox со списком множественного выбора. Про каждый пункт сказано, выбран он или нет, а скрытое статусное сообщение после каждого изменения называет итог, например «2 selected.».
- Одна остановка Tab. Чипы не остановки Tab; к ним переходят стрелками.
- Кнопка удаления на каждом чипе называется «Remove» плюс пункт, например «Remove Red».
- Кнопка «очистить всё» называется «Clear all». Это не остановка Tab; с клавиатуры чипы удаляют по одному.
- Меню не закрывается, пока человек выбирает, и Esc никогда не меняет выбор.
- При увеличении меню остаётся на экране. Каждая строка не ниже 32px.
Ваша часть:
- Дайте каждому multiselect имя: подпись Field,
aria-labelилиfilter-label. - Если есть предел, задайте
maxи назовите его в подсказке. - Пишите ошибки по правилам из раздела «Текст».
| Клавиша | Меню закрыто | Меню открыто |
|---|---|---|
Down / Up |
открывает меню и подсвечивает первый пункт | двигает подсветку |
Enter |
открывает меню; с searchable отправляет форму |
отмечает или снимает подсвеченный пункт |
Space |
открывает меню; с searchable вводит пробел |
отмечает или снимает подсвеченный пункт; с searchable вводит пробел |
| буквы | с searchable: ввод и фильтрация |
с searchable: ввод и фильтрация |
Left |
переходит к последнему чипу (с searchable — из начала текста) |
так же |
Backspace |
удаляет последний чип (с searchable — когда текст пуст) |
так же |
Esc |
— | закрывает меню; выбор сохраняется |
На чипе: Left / Right переходят между чипами, Right после последнего возвращает в
поле, Home ведёт к первому чипу, End возвращает в поле, а Delete или Backspace
удаляют чип.
Пределы контраста
- Рамка поля: 1.33:1 в светлой теме и 2.00:1 в тёмной, ниже минимума 3:1; 4.22:1 в высококонтрастной. Всегда показывайте подпись.
- Placeholder: 3.26:1 в светлой теме, ниже минимума 4.5:1 для текста. Не пишите в нём ничего нужного.