Select
One value from a list of six or more options, with search above ten.
When to use
Section titled “When to use”- Models, providers, engines, time zones, agents: lists the user did not write.
- Up to five short options: use
RadioorSegmentedControl, so every option is visible. - Many values at once: use
MultiSelector aChipgroup. - Options that are brands (models by provider, connectors): use
BrandSelect. It adds the official mark to every row.
What the consumer provides
Section titled “What the consumer provides”label,description, and aplaceholderwritten as an instruction (“Choose an engine”).data:{ value, label, description?, group?, disabled? }[].descriptionis short secondary text after the label, such as the provider of a model. Put the recommended option first and say why in the field description.searchableabove ten options.error, as inTextInput.- An optional
leftSectionicon aticon-sm, such asworldfor a time zone.
Behaviour
Section titled “Behaviour”- The native
selectelement is not used. The control is a custom combobox, so the open list matches the system. - The trigger is a
TextInput: same height, radius, border, focus ring, invalid state and disabled state. It shows the chosen label, or the placeholder inink-muted. - A chevron-down at
icon-sminink-mutedsits on the right. It turns 180° when the list opens, induration-base. While the list is open, the trigger border isemerald-deep. - The list is a
Menupanel:surface-100,border,radius-lg,shadow-sm, 4px padding, 4px below the trigger. It fades in and rises 4px induration-enter, and leaves induration-leave. Under reduced motion it only fades, induration-fast. - Rows are 36px with
radius-sm. Eight rows show, then the list scrolls. Hover and the keyboard-active row usesurface-hover. Group labels use theoverlinestyle. - The chosen row has
emerald-deeptext and the caret on the right. The caret takes the place of the Tablercheckicon, as everywhere in Upmo. - Secondary text follows the label in
ink-mutedat 13px. When space is short, the secondary text is cut first. - A disabled option stays visible in
ink-disabled. The keyboard and the mouse skip it. - With search, a filter input with the
searchicon sits at the top of the panel. It takes focus when the list opens. It matches the label and the secondary text. A group with no match hides. With no match at all, the panel says “No matches”. - Picking an option closes the list and returns focus to the trigger. Escape closes it the same way, with no change. A click outside or Tab also closes it.
Keyboard
Section titled “Keyboard”- Closed: ArrowDown, ArrowUp, Enter and Space open the list on the chosen option. Without search, typing a letter opens the list on the first match.
- Open: ArrowDown and ArrowUp move one row. Home and End jump to the first and last row. Enter picks the active row. Space also picks when there is no filter input. Escape closes.
- Typeahead: type the first letters of a label to jump to it. Letters typed within half a second count as one search. The same single letter again moves to the next match.
Accessibility
Section titled “Accessibility”- The trigger has
role="combobox",aria-haspopup="listbox",aria-expandedandaria-controls. Its name comes from the label. - The list has
role="listbox". Each row hasrole="option"andaria-selected. A group hasrole="group"andaria-labelledbypointing to its label. - Focus stays on the trigger, or on the filter input when there is one.
aria-activedescendantpoints to the active row. - An invalid field sets
aria-invalidandaria-describedbyon the trigger. “No matches” is astatuslive region.
Tokens
Section titled “Tokens”As TextInput, plus surface-100, border, radius-lg, radius-sm, shadow-sm, surface-hover, emerald-deep, ink-muted, ink-disabled, icon-sm, z-dropdown, duration-base, duration-enter, duration-leave, duration-fast, ease-standard, ease-leave.
Mantine
Section titled “Mantine”<Select label data searchable checkIconPosition="right" nothingFoundMessage="No matches" /> with classNames={{ input: 'u-input u-select-trigger', dropdown: 'u-select-drop', option: 'u-opt' }}. Set the panel with comboboxProps={{ offset: 4, shadow: 'sm', radius: 'lg', transitionProps }}: transitionProps is a 4px fade-down with duration from duration-enter and exitDuration from duration-leave. renderOption draws the label, the secondary text and the caret. Rotate the chevron in rightSection while the dropdown is open.
Mantine Select searches in the trigger itself. For the filter input at the top of the panel, build on Combobox with Combobox.Search, Combobox.Options, Combobox.Group and Combobox.Empty.