Select
A labelled choice from a list, built on a native <select>.
import { Select } from '@zitdevs/ui';Examples
Section titled “Examples”A plain select is the native control, styled. It needs no script at all.
A range is enough.
Choose a budget range.
Searchable
Section titled “Searchable”For a long list, searchable adds the design’s filterable list. These demos are server HTML; the
page runs enhanceSelects(document) from @zitdevs/ui/enhance, as the Astro website does. Type to
filter — accents and case do not matter — move with the arrow keys, choose with Enter or a click,
close with Escape.
- Colombia
- Dominican Republic
- Mexico
- Peru
- Spain
- United States
- No results
- Colombia
- España
- México
- Perú
- República Dominicana
- Sin resultados
<Select id="country" label="Country" options={countries} placeholder="Choose a country" autoComplete="country-name" searchable/>Controlled
Section titled “Controlled”A caller that holds the choice passes value and onValueChange. Searchable, a choice only asks:
the trigger changes when value does, and the native select fires change for the form. Plain,
it is a controlled native select.
'use client';
const [country, setCountry] = useState('Peru');
<Select id="country" label="Country" options={countries} searchable value={country} onValueChange={setCountry}/>;| Prop | Type | Default | Notes |
|---|---|---|---|
id |
string |
required | The native select’s id. |
name |
string |
id |
Submitted under it. |
label |
string |
required | |
options |
readonly SelectOption[] |
required | A string, or { value, label?, disabled?, lang? }. |
autoComplete |
string |
— | What the answer is, such as country-name, whenever it asks about the reader. |
defaultValue |
string |
— | Matched against values, not labels. A new one starts the select over. |
value |
string |
— | The chosen value, for a caller that holds it. |
onValueChange |
(value: string) => void |
— | The value the reader chooses. Controlled, it asks; uncontrolled, it reports. |
placeholder |
string |
— | Shown while nothing is chosen; never submitted. |
requirement |
'none' | 'required' | 'optional' |
'none' |
As on Field. |
hint, error |
string |
— | The error replaces the hint and marks the control invalid. |
messageId |
string |
`${id}-message` |
For a form whose ids are fixed. |
disabled |
boolean |
false |
|
searchable |
boolean |
false |
Adds the filterable list. Needs the enhancer. |
searchPlaceholder |
string |
labels.search |
Names the filter field. |
labels |
Partial<SelectLabels> |
English | { optional, search, noResults }. es.select. |
ref |
Ref<HTMLSelectElement> |
— | The native select. |
className |
string |
— | Placement, on the wrapper. |
| anything else | <select> attributes |
— | On the native select: form, onChange (it fires for the list’s choices too). |
Exported types: SelectProps, SelectOption, SelectLabels, and the label sets selectLabels.
From @zitdevs/ui/enhance: enhanceSelects, SelectOptions, SelectEnhancement.
Accessibility
Section titled “Accessibility”- Plain, it is the native select: every platform’s own picker, keyboard and screen reader support.
- Searchable, it follows the ARIA combobox pattern: a button that opens a listbox, with a filter
field that points at the active option through
aria-activedescendant. Arrow keys, Home and End move; Enter chooses; Escape closes and returns focus; Tab and a click elsewhere close. - The native select stays in the form, named and enabled, so it validates and submits; it is taken off screen and out of the accessibility tree while the list stands in for it. A click on the label or a failed check sends focus to the control the reader can see, and a failed check marks it invalid until a valid choice.
- An answer in another language carries its
lang(WCAG 3.1.2), in the native and the listed option. - A placeholder is a disabled first option: shown, never submitted.
Without JavaScript
Section titled “Without JavaScript”A plain select needs none. A searchable one is the plain native select until the enhancer runs, which is a complete control on its own:
- React: nothing to do. The select’s root is a small client part that runs the enhancer once it
has hydrated. A plain
Selectships no JavaScript. - No React: load
@zitdevs/ui/enhanceand callenhanceSelects(document)once. A page that also hydrates a Select island scopes the call to its static part.
Theming
Section titled “Theming”fieldControl’s roles, muted for the prompt, ink and border-strong for the markers;
surface-raised and border-default for the list, surface for the filter until it takes focus,
primary-soft for the active option and primary for the check.