Skip to content

Combobox

SelectionSince 0.1.0
import { Combobox } from '@absoluteui/components/combobox';
import '@absoluteui/components/combobox/style.css';

Using Angular? See Combobox for Angular.

An editable single-select combobox with list autocomplete (WAI-ARIA 1.2), without automatic selection.

  • DOM focus stays on the input; the active option is conveyed through aria-activedescendant.
  • The listbox renders in the browser top layer (Popover API) and is positioned by the positioning engine.
  • Choosing one value from a list long enough to benefit from filtering (roughly 7+ options).
  • Forms that submit a single option value.
  • Fewer than ~7 options: use a radio group or native select.
  • Free-text entry with suggestions (custom values are not supported).
  • Multiple selection: use Multi Select (or Checkbox Group for a few options).
import { Combobox, type ComboboxChangeDetail } from '@absoluteui/components/combobox';
const host = document.createElement('div');
document.body.append(host);
const country = Combobox.create(host, {
label: 'Country',
name: 'country',
options: [
{ value: 'in', label: 'India' },
{ value: 'jp', label: 'Japan' },
{ value: 'de', label: 'Germany' },
],
});
host.addEventListener('aui-change', (event) => {
const { value } = (event as CustomEvent<ComboboxChangeDetail>).detail;
country.update({ invalid: value === null });
});
import { Combobox } from '@absoluteui/components/combobox';
const host = document.createElement('div');
document.body.append(host);
Combobox.create(host, {
label: 'Priority',
required: true,
invalid: true,
errorMessage: 'Choose a priority.',
options: [
{ value: 'high', label: 'High' },
{ value: 'low', label: 'Low' },
],
});
import { Combobox, type ComboboxChangeDetail } from '@absoluteui/components/combobox';
import '@absoluteui/components/combobox/style.css';
const host = document.querySelector<HTMLElement>('#fruit');
if (host) {
Combobox.create(host, { label: 'Fruit', options: [{ value: 'apple', label: 'Apple' }] });
host.addEventListener('aui-change', (e) => {
console.log((e as CustomEvent<ComboboxChangeDetail>).detail.value);
});
}
import { Combobox } from '@absoluteui/components/combobox';
const host = document.createElement('div');
document.body.append(host);
Combobox.create(host, {
label: 'Country',
name: 'country',
options: [
{ value: 'in', label: 'India' },
{ value: 'jp', label: 'Japan' },
],
});
<div class="aui-combobox"> (consumer host)
<label class="aui-combobox__label" for=input>
<div class="aui-combobox__control">
<input role="combobox" class="aui-combobox__input">
<button class="aui-combobox__toggle" tabindex="-1" aria-label="Show options">
</div>
<div class="aui-combobox__description"> <div class="aui-combobox__error">
<input type="hidden" name>
<div class="aui-combobox__popup" popover="manual">
<div role="listbox" class="aui-combobox__listbox"> <div role="option">... </div>
<div class="aui-combobox__empty">No results</div>
</div>
</div>
Combobox.create(host: HTMLElement, options: ComboboxOptions): Combobox

Global configuration key: combobox (see Configuration).

Option Type Default Description
label string Visible label and accessible name. Required.
options readonly ComboboxOption[] { value, label, disabled?, description? } with unique values. Required.
value string | null null Selected value.
name string Form field name for the hidden input.
placeholder string Input placeholder (supplementary; never replaces the label).
description string Help text referenced by aria-describedby.
required boolean false Sets required and aria-required.
disabled boolean false Disables the input and toggle.
readOnly boolean false Prevents opening and editing.
invalid boolean false Sets aria-invalid and shows errorMessage.
errorMessage string Validation message shown when invalid.
icons Partial<{ expand: IconRef; selected: IconRef; error: IconRef }> Icon slot overrides.
renderOption (option: ComboboxOption, context: ComboboxRenderContext) => Node Custom option content (since 0.9.0); the label stays the accessible name. Nodes are released with releaseNode when dropped.
loadOptions (query: string, context: ComboboxLoadContext) => Promise<readonly ComboboxOption[]> Load options for the typed text instead of filtering options (since 0.9.0); debounced, stale requests aborted, loading and errors announced.
loadDelay number "250" Milliseconds between the last keystroke and loadOptions (0-5000).
filter 'contains' | 'startsWith' | 'none' "contains" Filtering strategy. Globally configurable.
openOnFocus boolean false Open the listbox on focus/click. Globally configurable.
clearOnEscape boolean true Escape with a closed listbox clears the value. Globally configurable.
locale string "en-US" Messages and filtering locale. Globally configurable. Inherited from common configuration.
direction 'ltr' | 'rtl' "ltr" Popup alignment direction. Globally configurable. Inherited from common configuration.
Property Type Description
host HTMLElement Consumer-provided host (children are owned by the component).
input HTMLInputElement The combobox input.
listbox HTMLDivElement The listbox element.
value string | null Selected value.
selectedOption ComboboxOption | null Selected option.
isOpen boolean Whether the listbox is shown.
validity FieldValidity { valid, error, message }. invalid/errorMessage options take precedence over the required check.
name string | null Form field name.
label string Label text.
disabled boolean Disabled state.
dirty boolean Value differs from the initial value.
touched boolean Focus has left the control at least once.
Method Description
setValue(value: string | null): void Programmatic selection (no aui-change). Unknown values clear the selection.
update(options: Partial<ComboboxOptions>): void Merge and apply options.
open(): void Show the listbox.
close(): void Hide the listbox.
destroy(): void Close, remove listeners, subscriptions, and rendered children.
reportValidity(): boolean Show messages now and return validity.
setCustomError(message: string | null): void External error (server, cross-field rule); cleared when the user changes the value.
focus(): void Focus the control.
reset(): void Restore the initial value and clear touched/submitted state (no aui-change).
getFormValue(): string | null Selected value for form serialization.
Event Detail Bubbles Cancelable Description
aui-change { value: string | null; option: ComboboxOption | null } Yes No User changed the selection.
aui-input { query: string } Yes No User edited the input text.
aui-open null No No Listbox opened.
aui-close null No No Listbox closed.

All rules live in the aui.components cascade layer, so application styles override them without specificity battles.

Class Purpose
aui-combobox Host.
aui-combobox--invalid | --disabled State modifiers on the host.
aui-combobox__label Visible label.
aui-combobox__control Input and toggle wrapper (popup anchor).
aui-combobox__input Combobox input.
aui-combobox__toggle Show-options button (not in tab order).
aui-combobox__popup Top-layer popover container.
aui-combobox__listbox Listbox.
aui-combobox__option Option; –active marks the active descendant.
aui-combobox__empty Empty-results message.
aui-combobox__description | __error Help and error text.
Slot Default icon
expand chevron-down
selected check
error alert-circle

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-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-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

interface ComboboxOption { readonly value: string; readonly label: string; readonly disabled?: boolean; readonly description?: string }
interface ComboboxChangeDetail { value: string | null; option: ComboboxOption | null }
type ComboboxFilter = 'contains' | 'startsWith' | 'none';

Pattern: WAI-ARIA APG Combobox pattern: editable combobox with list autocomplete, without automatic selection.

Key Context Action
ArrowDown Closed Opens; activates the selected option or the first enabled option.
ArrowUp Closed Opens; activates the selected option or the last enabled option.
Alt+ArrowDown Closed Opens without activating an option.
ArrowDown / ArrowUp Open Moves the active option, skipping disabled options, wrapping.
PageDown / PageUp Open Moves the active option by 10.
Enter Open with an active option Selects it, closes, emits aui-change.
Escape Open Closes the listbox; keeps the value.
Escape Closed with a value, clearOnEscape=true Clears the value.
Tab Any Closes the listbox and moves focus; free text reverts to the selected label.
Printable characters Any Edits the text, opens, and filters.
Element Role
input combobox
listbox container listbox
each option option
toggle button button (native, tabindex=-1)
Attribute Element Value
aria-autocomplete input list
aria-expanded input and toggle true when the listbox is shown
aria-controls input and toggle listbox id
aria-activedescendant input id of the active option; removed when none
aria-selected option true for the selected option
aria-disabled option true for disabled options
aria-invalid / aria-required / aria-describedby input Reflect invalid, required, description and error text

DOM focus always stays on the input; options are never focused. The active option has a visible outline.

Criterion Level Note
1.3.1 Info and Relationships A
1.4.1 Use of Color A
1.4.3 Contrast (Minimum) AA
1.4.11 Non-text Contrast AA
2.1.1 Keyboard A
2.4.7 Focus Visible AA
3.3.1 Error Identification A
3.3.2 Labels or Instructions A
4.1.2 Name, Role, Value A
4.1.3 Status Messages AA

Automated test results: Accessibility and compliance.

  • Result counts are announced via a polite live region after typing pauses.
  • The toggle button is excluded from the tab order per the APG example; it has a localized accessible name.
  • Manual screen-reader verification (NVDA, JAWS, VoiceOver, TalkBack) is pending.

closed, open, filtering, empty results, option active, option selected, option disabled, disabled, read-only, invalid, required.

From Trigger To
closed ArrowDown/ArrowUp, typing, toggle click open
open Enter on an active option / option click closed + selected (aui-change)
open Escape, Tab, outside pointer, blur closed (value unchanged; text reverts to the selected label)
closed (has value) Escape with clearOnEscape cleared (aui-change with null)
  • No automatic selection. Typing never changes the value until the user confirms.
  • Result counts are announced in a shared polite live region, 400 ms after typing stops.
  • The active option is outlined, so it does not rely on color alone (WCAG 1.4.1).
  • Verified with axe (WCAG 2.2 AA) while open in 3 themes.
  • Manual screen-reader verification is pending.

The input fills its container, and the listbox matches the input width. The listbox flips above the input when space below is insufficient; this is verified at a 375x667 viewport. Height is capped by min(popover-max-height, available space).

Logical properties throughout. Popup alignment follows the direction option or configuration.

Token-driven. In forced-colors mode, the selected option uses Highlight/HighlightText and the active outline uses Highlight.

configureAbsoluteUI({ components: { combobox: { filter: 'startsWith', openOnFocus: true } } });

Instance options override global and theme defaults. All configurable options are dynamic.

icons.expand (default chevron-down), icons.selected (default check), and icons.error (default alert-circle, since 0.2.0). Required fields show the shared visual-only asterisk.

Labels and descriptions use textContent. Positioning writes styles through the CSSOM only, so no style attributes are used. Verified under a strict CSP with Trusted Types enforced.

No persistence, network access, or telemetry. The hidden input holds the selected value when name is set.

Filtering is O(n) per keystroke using a normalized comparison: NFD form, diacritics removed, locale lowercase. All filtered options are rendered.

Budgets in headless Chromium:

  • create with 1,000 options: under 100 ms;
  • filter keystroke with 1,000 options: under 50 ms.

Tested with 1,000 options. Larger lists need virtualization, which is not available; use loadOptions to load matching options instead.

  • The popup is hidden by the browser until its first open, because popover="manual" is set at construction.
  • Inside a modal dialog, the listbox renders above the dialog, and Escape closes the listbox before the dialog.
  • Disabled options cannot be activated with the keyboard or clicked.
  • Updating options so the selected value no longer exists clears value, the input text, and the hidden input. No aui-change is emitted, because the change is programmatic.
Code Category Cause
COMBOBOX_OPTIONS configuration Missing label, non-array options, malformed option, or duplicate value.
CFG_INVALID_VALUE / CFG_UNKNOWN_OPTION configuration Invalid or unknown configuration.
  • Strict selection only; no custom values.
  • No virtualization or option groups; one value only (use Multi Select for several).
  • Substring or prefix matching only; no fuzzy search.
  • Manual screen-reader verification is pending.
  • Custom option templates.
  • Non-string option values.
  • Duplicate values.