Multiselect
A multiselect lets people choose several options from a list. Chosen options show as removable chips, and all of them submit with the form.
<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>Put the multiselect in a aria-label; otherwise it has no name at all.
Anatomy
| Part | What it is |
|---|---|
tk-multiselect |
the element; one Tab stop |
| field | holds the chips, and the search text when searchable; grows as chips wrap |
| chip | one chosen option, a removable |
| clear-all button | appears when something is chosen; removes every chip |
| chevron | turns over while the menu is open |
| menu | the options, as wide as the field; stays open while people choose |
| option | one row with a leading checkbox |
Variants
<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(default): a form field with chips. Name it with a Field oraria-label.filter: a compact button for a filter bar. It reads "Status" until something is chosen, then "Status (2)", and gets a tinted fill. The choices live in the menu; there are no chips.filter-labelis required: it is the visible text and the accessible name. Don't put a filter in a Field.
Searchable
<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 adds a text input that filters the menu as people type, the same way a
loadOptions loader turns
it on. The filter variant can't be searchable.
Sizes
<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 |
Min height | Padding-inline | Text | Option row |
|---|---|---|---|---|
medium (default) |
32px | space-8 |
Body Small (14/20) | 32px |
large |
36px | space-10 |
Body Medium (16/24) | 36px |
With chips, the field's leading padding shrinks to space-4 so the chips line up with
its edge. Chips wrap and the field grows; set max-rows to stop at about that many rows
and scroll the rest.
States
<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>
Hover doesn't change the field. Keyboard focus colors its border and thickens it; there is
no ring. While a chip has focus, the chip shows the ring instead. The filter variant
tints on hover and shows a focus ring.
disabled: can't be focused or opened, and isn't submitted. Chips can't be removed.readonly: shows the chips and submits them. The field stays focusable, but the menu doesn't open, chips can't be removed, and the clear-all button and chevron are hidden.- Error: set
erroron the Field. The message appears below and the border turns red, even while focused. Thefiltervariant never turns red.
Selection limit
<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 caps how many options people can choose. At the cap, the other options are disabled
and the menu says "You can choose up to 2." Chosen options can still be removed.
Options and data
- Write options as
<option>children, with<optgroup>for groups.selectedsets the starting choices. data-descriptionadds a short second line in the menu; chips show the label only.- Options can also come from the
optionsproperty or aloadOptionsloader, as inCombobox . A loader always shows the search input. creatableadds aCreate "…"row when a search matches nothing; choosing it adds a new chip. It follows theCombobox rules and stops atmax.
When to use
- Use a multiselect when people can pick several options from a list too long to show in full.
- Use a
Checkbox group for a short set that fits on screen. It is simpler and shows every option. - Use a
Select orCombobox when people pick exactly one.
Content
- Label: name what you're asking for, in sentence case, with no colon.
- Placeholder: "Choose…". It disappears once something is chosen, so don't put anything people need in it.
- Options: one short line each, in an order people can guess. Put the distinguishing word first. Don't start them with an icon or emoji.
- Limit: if you set
max, say so in the helper: "Choose up to 3". - Error: say how to fix it, using the label's words: "Choose at least one topic".
More in
Do / Don't
| ✅ Do | ❌ Don't |
|---|---|
| Use a checkbox group for a short set | Hide a few options inside a multiselect |
Add searchable for long lists |
Make people scroll a long list to find options |
Set max and say it in the helper |
Drop extra choices without saying why |
| Label it with a Field | Use the placeholder as the label |
| Put the distinguishing word first | Start options with icons or emoji |
Related
Field : the label, helper, and error around a multiselect.Checkbox group : a short set, all visible.Select : one option from a short list; it has the samefiltervariant.Combobox : one option from a long list, with the same filtering.Tag : the chip a multiselect uses.Select family : how these compare.
API
Attributes
| Attribute | Type | Default | What it does |
|---|---|---|---|
placeholder |
string | — | the hint shown while nothing is chosen |
name |
string | — | the form field name; each chosen value submits under it |
required |
boolean | false |
an empty choice blocks form submission; no visible marker (the Field adds the asterisk) |
disabled |
boolean | false |
not focusable, can't open, left out of the form |
readonly |
boolean | false |
focusable and submitted, but can't open or change |
size |
medium | large |
medium |
height 32 / 36px |
variant |
field | filter |
field |
a form field or a filter-bar button |
filter-label |
string | — | required with variant="filter": the button's text and accessible name |
searchable |
boolean | false |
adds a search input that filters the menu |
creatable |
boolean | false |
lets people add an option that isn't in the list |
max |
number | — | the most options people can choose |
max-rows |
number | — | the chip rows shown before the field scrolls |
min-chars |
number | 0 |
with loadOptions: characters needed before loading |
debounce |
number | 250 |
with loadOptions: milliseconds to wait after typing |
Properties
| Property | Type | What it does |
|---|---|---|
value |
string[] |
the chosen values, in the order they were chosen |
options |
{ value, label, description?, disabled?, group? }[] |
options from JavaScript; replaces the <option> children |
loadOptions |
(query, { signal }) => Promise<options> |
loads options per query; turns on the search input |
Slots
| Slot | Content |
|---|---|
| default | <option> and <optgroup> elements; changes to them are picked up |
Events
change fires when someone checks or unchecks an option, removes a chip, clears all, or
creates an option. It doesn't fire when you set value in code. Read the new value from
value.
In a form, each chosen value submits under name, like a checkbox group, and a form reset
restores the starting choices.
CSS
::part(field): the field, or the button in thefiltervariant.::part(listbox): the menu.
Tokens
The field, menu, options, error, and focus use
| Part | Tokens |
|---|---|
| option checkbox | surface/default + border/subtle, radius-4; highlighted row border/strong |
| option checkbox, checked | fill/accent + icon/accent/on-fill check; highlighted row fill/accent/hover |
| option checkbox, disabled | surface/disabled + border/disabled, check icon/disabled |
| filter, hover | state/hover |
| filter, chosen | state/on + border/selected (state/on-hover under the pointer) |
Accessibility
Built in:
- Screen readers hear a combobox with a multi-select list. Each option reads as selected or not, and a hidden status message announces the total, such as "2 selected.", after each change.
- One Tab stop. Chips aren't Tab stops; people reach them with the arrow keys.
- Each chip's remove button is named "Remove" plus the option, such as "Remove Red".
- The clear-all button is named "Clear all". It isn't a Tab stop; from the keyboard, remove chips one by one.
- The menu stays open while people choose, and Esc never changes the choice.
- The menu stays on screen when zoomed. Every row is at least 32px tall.
Your part:
- Give every multiselect a name: a Field label,
aria-label, orfilter-label. - If there is a limit, set
maxand say it in the helper. - Write errors as the Content rules say.
| Key | Menu closed | Menu open |
|---|---|---|
Down / Up |
opens the menu and highlights the first option | moves the highlight |
Enter |
opens the menu; with searchable, submits the form |
checks or unchecks the highlighted option |
Space |
opens the menu; with searchable, types a space |
checks or unchecks the highlighted option; with searchable, types a space |
| letters | with searchable: type and filter |
with searchable: type and filter |
Left |
moves to the last chip (with searchable, from the start of the text) |
same |
Backspace |
removes the last chip (with searchable, when the text is empty) |
same |
Esc |
— | closes the menu; the choice stays |
On a chip: Left / Right move between chips, Right past the last returns to the
field, Home jumps to the first chip, End returns to the field, and Delete or
Backspace removes the chip.
Contrast limits
- Field border: 1.33:1 in light and 2.00:1 in dark, under the 3:1 minimum; 4.22:1 in high contrast. Always show a label.
- Placeholder: 3.26:1 in light, under the 4.5:1 text minimum. Don't put anything people need in it.