Select
import { Select } from '@absoluteui/components/select';import '@absoluteui/components/select/style.css';Using Angular? See Select for Angular.
What it does
Section titled “What it does”Single choice from a known, flat list of options. Select implements the WAI-ARIA APG select-only combobox: a focusable role="combobox" element shows the selected label (or a placeholder) and opens a role="listbox" popup in the top layer. It adds typeahead, option descriptions, localized required validation through the shared validation engine, and native form submission through a hidden input.
When to use
Section titled “When to use”- Choosing exactly one value from a known list of roughly 5 to 1,000 options.
- Space is limited and the options do not need to be visible all the time.
- A consistent, styleable alternative to the native <select> with option descriptions and localized validation.
When not to use
Section titled “When not to use”- Two to five options that should stay visible: use Radio Group.
- Users need to type to filter or search a long list: use Combobox.
- Multiple selection: use Checkbox Group or a multi-select pattern.
- Actions rather than values: use Menu.
Examples
Section titled “Examples”Required select in a form
Section titled “Required select in a form”import { Select } from '@absoluteui/components/select';
const host = document.createElement('div');document.body.append(host);const country = Select.create(host, { label: 'Country', name: 'country', required: true, options: [ { value: 'in', label: 'India' }, { value: 'gb', label: 'United Kingdom' }, { value: 'us', label: 'United States' }, ],});host.addEventListener('aui-change', () => { host.dataset.valid = String(country.validity.valid);});Option descriptions, disabled options, and custom validation
Section titled “Option descriptions, disabled options, and custom validation”import { Select, type SelectChangeDetail } from '@absoluteui/components/select';
const host = document.createElement('div');document.body.append(host);Select.create(host, { label: 'Plan', value: 'team', options: [ { value: 'free', label: 'Free', description: 'Up to 3 projects' }, { value: 'team', label: 'Team', description: 'Unlimited projects' }, { value: 'enterprise', label: 'Enterprise', description: 'Contact sales', disabled: true }, ], validate: (value) => (value === 'free' ? 'The free plan does not include SSO.' : null),});host.addEventListener('aui-change', (event) => { const detail = (event as CustomEvent<SelectChangeDetail>).detail; host.dataset.plan = detail.value ?? '';});Show messages only on submit
Section titled “Show messages only on submit”import { configureAbsoluteUI } from '@absoluteui/core';
configureAbsoluteUI({ components: { select: { validationTiming: 'submit' } } });import { Select, type SelectChangeDetail } from '@absoluteui/components/select';import '@absoluteui/components/select/style.css';
const host = document.querySelector<HTMLElement>('#plan');if (host) { const plan = Select.create(host, { label: 'Plan', name: 'plan', required: true, options: [ { value: 'free', label: 'Free', description: 'Up to 3 projects' }, { value: 'team', label: 'Team', description: 'Unlimited projects' }, ], }); host.addEventListener('aui-change', (event) => { const { value } = (event as CustomEvent<SelectChangeDetail>).detail; if (value === null) plan.reportValidity(); });}More usage
Section titled “More usage”import { Select } from '@absoluteui/components/select';
const host = document.createElement('div');document.body.append(host);Select.create(host, { label: 'Country', name: 'country', required: true, options: [ { value: 'in', label: 'India' }, { value: 'gb', label: 'United Kingdom' }, ],});Anatomy
Section titled “Anatomy”<div class="aui-field aui-select" data-aui-control> (consumer host) <label class="aui-field__label" id=label>Label<span class="aui-field__required" aria-hidden="true">*</span></label> <div class="aui-select__combobox" role="combobox" tabindex="0" aria-haspopup="listbox" aria-expanded aria-controls=listbox aria-labelledby="label combobox"> <span class="aui-select__value">Selected label or placeholder</span> <span class="aui-select__chevron"><svg aria-hidden="true"></span> </div> <div class="aui-field__description"> (optional) <div class="aui-field__error aui-validation-message"> (when shown) <input type="hidden" name> (value for native forms) <div class="aui-select__popup" popover="manual"> (top layer when open) <div class="aui-select__listbox" role="listbox" aria-labelledby=label> <div class="aui-select__option" role="option" aria-selected [aria-disabled]> <span class="aui-select__option-label"> <span class="aui-select__option-description"> <svg class="aui-select__check"></div>API reference
Section titled “API reference”Factory
Section titled “Factory”Select.create(host: HTMLElement, options: SelectOptions): SelectGlobal configuration key: select (see Configuration).
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
label |
string |
Visible label and accessible name (via aria-labelledby). Required. | |
hideLabel |
boolean |
false |
Visually hide the label; it remains the accessible name. |
options |
readonly SelectOption[] |
{ value, label, disabled?, description? } with unique string values (flat list). Required. | |
value |
string | null |
null |
Initial selected value; also the value restored by reset(). Unknown values select nothing. |
name |
string |
Form field name for the hidden input. | |
placeholder |
string |
Shown while nothing is selected. Default: localized “Select an option”. | |
description |
string |
Help text referenced by aria-describedby. | |
required |
boolean |
false |
No selection is invalid; sets aria-required and shows the required indicator. |
disabled |
boolean |
false |
aria-disabled, removed from the tab order, not submitted. |
readOnly |
boolean |
false |
Focusable and submitted; the listbox does not open. |
validate |
(value: string | null) => string | null |
Consumer validation; return a message to mark the value invalid. | |
icons |
Partial<{ expand: IconRef; selected: IconRef; error: IconRef }> |
Icon slot overrides. | |
renderOption |
(option: SelectOption, context: SelectRenderContext) => Node |
Custom option content (since 0.9.0); the label stays the accessible name. Nodes are released with releaseNode when dropped. | |
renderValue |
(option: SelectOption, context: SelectRenderContext) => Node |
Custom content for the selected value (since 0.9.0); the label stays the announced value. | |
validationTiming |
'blur' | 'input' | 'submit' |
"blur" |
When validation messages first appear; afterwards they update live. Globally configurable. |
locale |
string |
"en-US" |
Placeholder and message locale. Globally configurable. Inherited from common configuration. |
direction |
'ltr' | 'rtl' |
"ltr" |
Listbox alignment direction (applied when the listbox opens). Globally configurable. Inherited from common configuration. |
Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
host |
HTMLElement |
Consumer host; its children are owned by the component. |
combobox |
HTMLDivElement |
The focusable role=combobox element. |
listbox |
HTMLDivElement |
The listbox element. |
value |
string | null |
Selected value. |
selectedOption |
SelectOption | null |
Selected option. |
isOpen |
boolean |
Whether the listbox is shown. |
name |
string | null |
Form field name. |
label |
string |
Label text. |
disabled |
boolean |
Disabled state. |
validity |
FieldValidity |
{ valid, error, message }; computed on every change. |
dirty |
boolean |
Value differs from the initial value. |
touched |
boolean |
Focus has left the control at least once. |
options |
Readonly<SelectOptions> |
Current instance options. |
Methods
Section titled “Methods”| Method | Description |
|---|---|
setValue(value: string | null): void |
Programmatic selection (no events); unknown values clear it; clears an external custom error. |
update(options: Partial<SelectOptions>): void |
Merge and apply options. A value whose option was removed is cleared. |
open(): void |
Show the listbox; the selected (or first enabled) option becomes active. |
close(): void |
Hide the listbox without changing the value. |
reportValidity(): boolean |
Show messages now and return validity. |
setCustomError(message: string | null): void |
External error (server, cross-field rule); cleared when the user selects a new value. |
getFormValue(): string | null |
Value for form serialization. |
focus(): void |
Focus the combobox. |
reset(): void |
Close, restore the initial value, and clear dirty/touched/submitted state. |
destroy(): void |
Close, remove rendering, listeners, registration, and subscriptions. |
Events
Section titled “Events”| Event | Detail | Bubbles | Cancelable | Description |
|---|---|---|---|---|
aui-change |
{ value: string | null; option: SelectOption | null; validity: FieldValidity } |
Yes | No | User selected a different option. |
aui-open |
null |
No | No | Listbox opened. |
aui-close |
null |
No | No | Listbox closed. |
CSS classes
Section titled “CSS classes”All rules live in the aui.components cascade layer, so application styles override them without specificity battles.
| Class | Purpose |
|---|---|
aui-field aui-select |
Host. |
aui-select--invalid | --disabled |
State modifiers on the host. |
aui-field__label | __required | __description | __error |
Shared field chrome. |
aui-select__combobox |
Focusable combobox; control boundary and popup anchor. |
aui-select__value | __value--placeholder |
Selected label or placeholder. |
aui-select__chevron |
Decorative expand icon. |
aui-select__popup |
Top-layer popover container. |
aui-select__listbox |
Listbox. |
aui-select__option |
Option; –active marks the active descendant. |
aui-select__option-label | __option-description |
Option text parts. |
aui-select__check |
Check icon on the selected option. |
Icon slots
Section titled “Icon slots”| Slot | Default icon |
|---|---|
expand |
chevron-down |
selected |
check |
error |
alert-circle |
Design tokens
Section titled “Design tokens”Tokens this stylesheet reads (verified in CI). Override them in a theme or with the Theme Designer.
--aui-border-width | --aui-color-border | --aui-color-border-strong | --aui-color-disabled-bg | --aui-color-error-fg | --aui-color-focus-ring | --aui-color-hover | --aui-color-selected | --aui-color-selected-indicator | --aui-color-selected-text | --aui-color-surface | --aui-color-surface-sunken | --aui-color-text | --aui-color-text-disabled | --aui-color-text-muted | --aui-control-height-md | --aui-control-height-sm | --aui-control-padding-x | --aui-duration-fast | --aui-easing-standard | --aui-focus-ring-offset | --aui-focus-ring-width | --aui-font-family-sans | --aui-font-size-md | --aui-font-size-sm | --aui-font-weight-medium | --aui-font-weight-regular | --aui-icon-size-sm | --aui-line-height-normal | --aui-line-height-tight | --aui-popover-max-height | --aui-radius-md | --aui-radius-sm | --aui-shadow-md | --aui-space-1 | --aui-space-2 | --aui-space-3
TypeScript definitions
Section titled “TypeScript definitions”interface SelectOption { readonly value: string; readonly label: string; readonly disabled?: boolean; readonly description?: string }interface SelectIcons { expand: IconRef; selected: IconRef; error: IconRef }type ValidationTiming = 'blur' | 'input' | 'submit';interface FieldValidity { readonly valid: boolean; readonly error: ValidationErrorCode | null; readonly message: string }interface SelectChangeDetail { value: string | null; option: SelectOption | null; validity: FieldValidity }Accessibility
Section titled “Accessibility”Pattern: WAI-ARIA APG Select-Only Combobox pattern: a non-editable combobox that controls a single-select listbox.
Keyboard
Section titled “Keyboard”| Key | Context | Action |
|---|---|---|
| Enter / Space / Alt+ArrowDown | Closed | Opens; activates the selected option or the first enabled option. |
| ArrowDown | Closed | Opens; activates the selected option or the first enabled option. |
| ArrowUp | Closed | Opens; activates the selected option or the last enabled option. |
| Home / End | Any | Opens if needed; activates the first / last enabled option. |
| Printable characters | Any | Typeahead: opens if needed and activates the next option whose label starts with the typed text; repeated characters cycle; Space continues a query in progress. |
| ArrowDown / ArrowUp | Open | Moves the active option, skipping disabled options, without wrapping. |
| PageDown / PageUp | Open | Moves the active option by 10. |
| Enter / Space / Alt+ArrowUp | Open | Selects the active option, closes, emits aui-change if the value changed. |
| Escape | Open | Closes without changing the value; does not reach a surrounding Dialog. |
| Tab / Shift+Tab | Open | Selects the active option, closes, and moves focus. |
| Tab / Shift+Tab | Closed | Moves focus to and from the combobox (not in the tab order when disabled). |
| Element | Role |
|---|---|
| combobox element (div, tabindex=0) | combobox |
| listbox container | listbox |
| each option | option |
| icons | none (aria-hidden=true) |
ARIA attributes
Section titled “ARIA attributes”| Attribute | Element | Value |
|---|---|---|
aria-labelledby |
combobox | Label id, then the combobox’s own id (name = label + selected label) |
aria-haspopup |
combobox | listbox |
aria-expanded |
combobox | true when the listbox is shown |
aria-controls |
combobox | listbox id |
aria-activedescendant |
combobox | id of the active option while open; removed when closed |
aria-required / aria-readonly / aria-disabled |
combobox | “true” when required, readOnly, or disabled |
aria-describedby |
combobox | Help text and visible error message ids |
aria-invalid |
combobox | “true” only while an error message is shown |
aria-labelledby |
listbox | Label id |
aria-selected |
option | “true” for the selected option, “false” otherwise |
aria-disabled |
option | “true” for disabled options |
DOM focus always stays on the combobox; options are never focused (pointer interaction with the popup is prevented from moving focus). The combobox shows a focus-visible outline; the active option has a visible outline that does not rely on color alone and is scrolled into view.
WCAG 2.2 success criteria addressed
Section titled “WCAG 2.2 success criteria addressed”Automated test results: Accessibility and compliance.
Notes and known caveats
Section titled “Notes and known caveats”- The label is a <label> associated through aria-labelledby because a div is not labelable; clicking the label focuses the combobox.
- The required asterisk is aria-hidden; required state is conveyed by aria-required.
- Errors appear after the first blur by default so users are not interrupted.
- Disabled selects use aria-disabled and tabindex=-1, per the APG guidance for disabled comboboxes.
- Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.
Behavior
Section titled “Behavior”States
Section titled “States”default, focus-visible, open, option active, option selected, option disabled, placeholder (no value), disabled, read-only, required, invalid (message shown).
State transitions
Section titled “State transitions”| From | Trigger | To |
|---|---|---|
| closed | Enter, Space, ArrowDown/Up, Alt+ArrowDown, Home, End, typing, or click | open, active option set |
| open | Enter, Space, Alt+ArrowUp, Tab, or option click | value set (aui-change if different), closed |
| open | Escape, outside pointer, focus leaves, or click on the combobox | closed, value unchanged |
| pristine | user selects a new value | dirty; message shown only with validationTiming: 'input' |
| any | focus leaves the host | touched; messages shown (blur timing) |
| any | reportValidity() (for example on form submit) |
submitted; messages shown for every timing |
| invalid (custom error) | user selects a new value | custom error cleared, validity recomputed |
| any | reset() |
closed, initial value; pristine, untouched |
Accessibility notes
Section titled “Accessibility notes”- DOM focus never leaves the combobox; pointer interaction with the popup does not move focus.
- The active option has an outline (not color alone) and is scrolled into view.
- A
<label>cannot name adiv, so it is associated througharia-labelledby; clicking it focuses the combobox. - Disabled selects use
aria-disabledand leave the tab order (tabindex="-1"), as in the APG guidance. - The required asterisk is
aria-hidden;aria-requiredconveys the state. Errors appear after the first blur by default. - Escape is arbitrated by the overlay stack, so inside a Dialog it closes only the listbox.
- Manual screen-reader verification is pending (see Known limitations).
Responsive behavior
Section titled “Responsive behavior”Full width of its container; the selected label truncates with an ellipsis. The listbox is at least as wide as the combobox, flips above when there is not enough space below, and caps its height to popover-max-height or the available space. Density tokens change the control height.
Right-to-left
Section titled “Right-to-left”Logical properties throughout: the chevron is at the inline end. The listbox aligns to the inline start of the combobox using the direction option or the inherited global direction, read when the listbox opens.
Theming
Section titled “Theming”Colors come from role tokens only. The selected option uses color-selected / color-selected-text with a color-selected-indicator check. In forced-colors mode the active outline and selected option use Highlight.
Global configuration
Section titled “Global configuration”configureAbsoluteUI({ components: { select: { validationTiming: 'submit' } } });Changes apply to existing instances immediately. locale and direction inherit from the common configuration.
Component configuration
Section titled “Component configuration”Instance options override global and theme defaults. All options are dynamic; direction applies the next time the listbox opens.
icons.expand (default chevron-down), icons.selected (default check), and icons.error (default alert-circle).
Security
Section titled “Security”All text uses textContent. Validation messages never include the selected value. Positioning writes styles through the CSSOM; no inline styles or HTML sinks. CSP-safe.
Data handling
Section titled “Data handling”No persistence, network access, or telemetry. A hidden input carries the selected value (empty string when nothing is selected) for native form submission when name is set; it is disabled, and therefore not submitted, while the select is disabled.
Performance
Section titled “Performance”All options are rendered once when options change; selection updates aria-selected in place. Seven listeners per instance and one configuration subscription; positioning runs only while open. Budget: Select.create with 1,000 options under 100 ms.
Supported data scale
Section titled “Supported data scale”1,000 options per select in the unit test (Select.testedOptionCount). Larger lists are untested; consider Combobox with filtering.
Edge cases
Section titled “Edge cases”- An initial or
setValuevalue that matches no option selects nothing; the placeholder is shown. - Removing the selected option through
update({ options })clears the value without an event. - A disabled option can still be the programmatic value; users cannot select it.
- Selecting the already-selected option closes the listbox without an
aui-change. - With no enabled options, opening shows the list with no active option.
Errors
Section titled “Errors”| Code | Category | Cause |
|---|---|---|
SELECT_OPTIONS |
configuration | Missing label, options not an array, an option without string value/label, duplicate values, or a non-string value. |
Validation (validity.error) |
validation | value-missing (message “Select an option.”), custom. |
A failed construction leaves no rendering, listeners, or subscriptions behind.
Known limitations
Section titled “Known limitations”- Flat list only; option groups are not available.
- No virtualization; lists above 1,000 options are untested.
- Single selection only.
- Typeahead matches label prefixes.
- Screen-reader behavior has not yet been manually verified with NVDA, JAWS, VoiceOver, and TalkBack.
Unsupported scenarios
Section titled “Unsupported scenarios”- HTML or custom content in options, the label, or messages.
- Free-text entry (use Combobox).
- Multiple selection.