Select
Select is a form field with one value from a list of options. The user opens the list from a button, and the list is a ListBox in a popover. It has the keyboard of a native select, a native control for the form, and one accessible name for the field and its value.
Anatomy
Select.Root holds the value and the open state. Select.Trigger is the button that opens the list, and Select.Value in it shows the selection. Select.Popover holds Select.List, and each Select.Item is one option.
Select.Label names the field. Select.ItemIndicator marks the selected option.
<script>
import { Select } from '@human-kit/ui';
</script>
<Select.Root name="fruit">
<Select.Label>Fruit</Select.Label>
<Select.Trigger>
<Select.Value />
</Select.Trigger>
<Select.Popover>
<Select.List>
<Select.Item id="apple">Apple</Select.Item>
<Select.Item id="banana">Banana</Select.Item>
</Select.List>
</Select.Popover>
</Select.Root>Select or Menu
A Menu is a list of commands: a press on an item does something, and nothing stays selected. A Select is a field: it holds a value, a form sends that value, and a screen reader announces the field with its value. Use Select when the user answers a question, and Menu when the user gives an order.
Value
Use bind:value to give the root your state. Use value with onChange and controlledValue to hold the state yourself. The root then reports each change, and it does not write value back. Thus you can refuse a change.
Use defaultValue when the root holds its own state.
The options are not in the DOM while the popover is closed. Give the same array to items on the root, thus the trigger reads the text of the value from it. Without items, the trigger shows the key until the popover opens one time.
More than one value
Set selectionMode="multiple". The popover then stays open after a selection, and value is an array. Select.Value shows the texts as a list in the language of the locale, and its snippet receives the array when you want a different text.
Keyboard
On the closed trigger:
ArrowDown,Enter,SpaceandAlt+ArrowDownopen the list. The focus goes to the selected option, or to the first option.ArrowUpopens the list. The focus goes to the selected option, or to the last option.HomeandEndopen the list on the first option and on the last option.- A letter or a digit opens the list on the first option that starts with it.
In the open list:
ArrowUpandArrowDownmove the focus. They stop at the ends, unlessloopis set.HomeandEndmove the focus to the first option and to the last option.PageUpandPageDownmove the focus ten options.- Typed characters move the focus to the option that starts with them. The same character again moves to the next option that starts with it.
Enter,SpaceandAlt+ArrowUpselect the option and close the list.Escapecloses the list without a change.Tabcloses the list without a change, and the focus continues past the trigger.
Forms
Give the root a name. The root makes a native <select> that the form reads, and that the browser autofill writes to. It is out of the tab order and hidden from assistive technology.
required, disabled and form go to that control. A form that reports the validity of a required select focuses the control, and the root moves the focus to the trigger.
Usage guidelines
- Put
Select.ValueinsideSelect.Trigger. The trigger then shows the selection, and the value is part of the accessible name of the field. - Give
itemstoSelect.Rootwhen the field starts with a value. - Use
disabledKeysfor options the user cannot select. The focus skips them. - A second press on the selected option changes nothing, and the popover closes. In the multiple mode a press toggles.
onOpenChangereceives the reason of each change, anddetails.cancel()refuses it.
Accessibility
Select.Triggeris a native<button>withrole="combobox",aria-haspopup="listbox",aria-expanded, andaria-controlswhile the list is open. This is what a native select is in the accessibility tree.- The name of the trigger is
Select.LabelandSelect.Value, in that order. A screen reader announces "Fruit, Apple, combo box". Anaria-labelledbyon the root replaces the label, and anaria-labelon the root is read in its place. Select.Labelis a native<label>for the trigger. A click on it moves the focus to the trigger, and it does not open the list.Select.Listhasrole="listbox"with the trigger as its name, and each option hasaria-selected. The list gets the DOM focus when the popover opens, on the selected option.aria-required,aria-readonlyandaria-invalidare on the combobox, which supports all three.- The popover is not a dialog. It has
role="presentation", and the listbox is what assistive technology lands on. - The focus returns to the trigger after
Escape, after a selection, and after a close by code. After an outside press, a scroll or aTab, it stays where the user put it. An outside press on nothing focusable is the exception: the focus goes back to the trigger, thus the keyboard can open the list again.
API reference
Root
The container. It holds the value and the open state, and it makes the native select for the form.
* required. Native HTML attributes of the underlying element are also accepted.
Label
The name of the field. It makes a label for the trigger, and it registers its id with the root.
* required. Native HTML attributes of the underlying element are also accepted.
Trigger
The button that opens the list. It makes a button with role="combobox", and it shows the value.
* required. Native HTML attributes of the underlying element are also accepted.
Value
The text of the selection inside the trigger, or the placeholder. It is part of the name of the field.
* required. Native HTML attributes of the underlying element are also accepted.
Popover
The floating panel that holds the list. It is Popover.Content with role="presentation".
* required. Native HTML attributes of the underlying element are also accepted.
List
The listbox inside the popover. It is ListBox.Root with the selection of the select, and it gets the focus when the popover opens.
* required. Native HTML attributes of the underlying element are also accepted.
Item
One option. It is ListBox.Item with an id that is unique across selects.
* required. Native HTML attributes of the underlying element are also accepted.
ItemIndicator
The mark of a selected option. It is in the DOM only while its option is selected, unless forceMount keeps it.
* required. Native HTML attributes of the underlying element are also accepted.