Skip to main content

Listbox

Implemented

Inline ARIA selection widget with full keyboard support (WAI-ARIA Listbox pattern 1.2). Single and multiple selection modes, option groups, and custom option rendering.

Categorymolecules
Sourcecomponents/molecules/Listbox/Listbox.tsx
AI HintAlways-visible ARIA listbox (WAI-ARIA 1.2) for single or multiple selection when every option should stay inline. Use Select (combobox trigger) for collapsed native dropdowns and SearchSelect for search-as-you-type; Listbox owns the always-expanded case.

Always-visible ARIA listbox (WAI-ARIA 1.2 pattern) for single and multiple selection. Full keyboard navigation, option groups, and custom option rendering. Use Select or SearchSelect when you need a collapsed trigger + popup instead.

Listbox

Form selection molecule

An inline selection list with role="listbox", aria-activedescendant tracking, wrap-around arrow navigation, and per-option disabled state. Controlled via value/onChange; ariaLabel is required for the accessible name.

  • General admission
  • VIPBackstage access
  • Early bird
  • Press
  • Friday
  • Saturday
  • Sunday

Props

Preview

Listbox

Prop
Type
Required
Default
Description
options
ListboxOption[]
Required
—

Array of options with value, label, disabled, group, description

mode
'single' | 'multiple'
Optional
'single'

Single or multi-select mode

value
string | string[]
Required
—

Selected value(s)

defaultValue
string | string[]
Optional
—

Initial uncontrolled value

onChange
(value: string | string[]) => void
Required
—

Callback when selection changes

size
'sm' | 'md' | 'lg'
Optional
'md'

Size variant

variant
'default' | 'bordered' | 'flush'
Optional
'default'

Visual style variant

emptyMessage
string
Optional
'No options'

Message shown when options array is empty

labels
ListboxLabels
Optional
—

i18n label overrides object (step-6 source fixes). labels.empty replaces the English-default emptyMessage ('No options') on localized surfaces — prefer it over overriding emptyMessage directly on /fr routes.

disabled
boolean
Optional
false

Disable the entire listbox

ariaLabel
string
Required
—

Accessible label for the listbox

ariaLabelledBy
string
Optional
—

ID of element that labels this listbox

maxHeight
number
Optional
240

Max height in pixels before scrolling

groups
Array<{ id: string; label: string }>
Optional
—

Option group headers

autoFocusFirstOption
boolean
Optional
false

Auto-focus first option on mount

renderOption
(option, state) => ReactNode
Optional
—

Custom option renderer

locale
string
Optional
—

lang attribute for per-option language annotation

className
string
Optional
—

Additional CSS classes

id
string
Optional
—

DOM id

Quick use:<Listbox />

Accessibility

ARIA rolelistbox

Keyboard

1 rule

WAI-ARIA 1.2 Listbox

Arrow Down/Up to navigate (wraps), Home/End to first/last enabled, PageUp/PageDown ±10, Enter/Space to select, Esc to blur

ARIA

1 rule

WAI-ARIA 1.2

role=listbox, aria-activedescendant for highlighted option, aria-multiselectable when mode=multiple

Usage

Import

tsxSnippet
import { Listbox } from "@epasslive/design-system"

Example 1

tsxSnippet
<Listbox options={options} value={selected} onChange={setSelected} ariaLabel="Ticket type" />

Example 2

tsxSnippet
<Listbox options={days} mode="multiple" value={picked} onChange={setPicked} ariaLabel="Event days" />

Anti-patterns

— Do not use for collapsed trigger+popup selection — Select or SearchSelect own that pattern

— Never omit value, onChange, or ariaLabel — the component is controlled and requires an accessible name

Design System v4.1.0 | ePassLive