Skip to content

Listbox

Listbox shows its options as a list that stays on the page. Click an option, or Tab to the list and use the arrow keys, then Space or Enter:

Rust
TypeScript
Go
COBOL

Chosen: rust

ListboxDemo.tsx
import { Paragraph, View } from '@hozo/core'
import { Listbox } from '@hozo/ui'
import { useState } from 'react'
const LANGUAGES = [
{ value: 'rust', label: 'Rust' },
{ value: 'ts', label: 'TypeScript' },
{ value: 'go', label: 'Go' },
{ value: 'cobol', label: 'COBOL', disabled: true },
] as const
export function ListboxDemo() {
const [chosen, setChosen] = useState<readonly string[]>(['rust'])
return (
<View className="w-full max-w-xs gap-3">
<Listbox<string>
options={LANGUAGES}
accessibilityLabel="Languages"
multiple
value={chosen}
onValueChange={setChosen}
/>
<Paragraph className="text-sm text-hozo-text-body">
{`Chosen: ${chosen.join(', ') || 'none'}`}
</Paragraph>
</View>
)
}
  • options: { value, label, disabled? } for each option.
  • multiple lets several be chosen. Then value and onValueChange hold an array, and the list announces aria-multiselectable. Without it, they hold one value.
  • value and onValueChange control it, or defaultValue leaves it uncontrolled.
  • The list is one tab stop. The arrows move between options, and typing a letter moves to the next option that starts with it. A disabled option is skipped.
  • The chosen options are tinted and bold, not tinted alone, because colour alone fails WCAG 1.4.1. The style comes from aria-selected, which is what a reader is told.
  • orientation is vertical by default. horizontal swaps the arrow keys.
  • In TypeScript, the option type is inferred from options and value. When it cannot be, as with multiple, give it: <Listbox<string> …>.