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, Space and Alt+ArrowDown open the list. The focus goes to the selected option, or to the first option.
  • ArrowUp opens the list. The focus goes to the selected option, or to the last option.
  • Home and End open 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:

  • ArrowUp and ArrowDown move the focus. They stop at the ends, unless loop is set.
  • Home and End move the focus to the first option and to the last option.
  • PageUp and PageDown move 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, Space and Alt+ArrowUp select the option and close the list.
  • Escape closes the list without a change.
  • Tab closes 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.Value inside Select.Trigger. The trigger then shows the selection, and the value is part of the accessible name of the field.
  • Give items to Select.Root when the field starts with a value.
  • Use disabledKeys for 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.
  • onOpenChange receives the reason of each change, and details.cancel() refuses it.

Accessibility

  • Select.Trigger is a native <button> with role="combobox", aria-haspopup="listbox", aria-expanded, and aria-controls while the list is open. This is what a native select is in the accessibility tree.
  • The name of the trigger is Select.Label and Select.Value, in that order. A screen reader announces "Fruit, Apple, combo box". An aria-labelledby on the root replaces the label, and an aria-label on the root is read in its place.
  • Select.Label is a native <label> for the trigger. A click on it moves the focus to the trigger, and it does not open the list.
  • Select.List has role="listbox" with the trigger as its name, and each option has aria-selected. The list gets the DOM focus when the popover opens, on the selected option.
  • aria-required, aria-readonly and aria-invalid are 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 a Tab, 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.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-disabled Present when the select is disabled.
data-focus-visible Present when the focus in the select came from the keyboard.
data-focus-within Present when the trigger or the list has the focus.
data-invalid Present when the value is invalid.
data-readonly Present when the select is read-only.
data-required Present when a form must have a value here.
data-select Identifies the root element.
data-select-hidden Identifies the wrapper of the native select. It is hidden from assistive technology.
data-state The open state of the popover: "open" or "closed".

Label

The name of the field. It makes a label for the trigger, and it registers its id with the root.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-disabled Present when the select is disabled.
data-select-label Identifies the label element.

Trigger

The button that opens the list. It makes a button with role="combobox", and it shows the value.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-invalid Present when the value is invalid.
data-placeholder Present while nothing is selected.
data-readonly Present when the select is read-only.
data-required Present when a form must have a value here.
data-select-trigger Identifies the trigger element.
data-state The open state of the popover: "open" or "closed".

Value

The text of the selection inside the trigger, or the placeholder. It is part of the name of the field.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-placeholder Present while nothing is selected.
data-select-value Identifies the value element.

Popover

The floating panel that holds the list. It is Popover.Content with role="presentation".

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-select-popover Identifies the popover element.

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.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-select-list Identifies the list element.

Item

One option. It is ListBox.Item with an id that is unique across selects.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-select-item Identifies the option element.

ItemIndicator

The mark of a selected option. It is in the DOM only while its option is selected, unless forceMount keeps it.

Prop Type Default

* required. Native HTML attributes of the underlying element are also accepted.

Data attribute Description
data-select-item-indicator Identifies the indicator element.
data-state The selection state of the option: "checked" or "unchecked".