Command from @prepared911/ui-core wraps cmdk with label, search, and grouped-listbox semantics. The library handles the combobox/listbox wiring and focus flow between the input and results; your job is to supply descriptive labels, ensure results announce, and keep empty states informative.
What the component handles
- Listbox semantics. Input →
combobox; CommandList → listbox; items → option.
- Focus flow. Focus starts in the input; arrow keys move through filtered results without pulling focus out of the input.
- Filter updates. Active option changes as results shrink or reorder;
aria-activedescendant keeps assistive tech in sync.
label on CommandList. Without it, screen-reader rotor surfaces an unnamed listbox. Pass something descriptive of the context ("Create incident", "Navigate to…").
- Accessible names on items. Visible text is the default source. If an item renders icons or complex children, ensure the accessible name still reads as a short, actionable phrase.
- Empty state announcement. When the search yields no results, render a
CommandEmpty region with text explaining next steps; don't leave the list silent.
- Confirmation for destructive actions.
Command items can trigger anything. Destructive items should hand off to a Modal confirmation rather than resolving inline.
- Type filters results.
- Arrow Up / Down moves the highlighted option; Home / End jump to first / last.
- Enter activates the highlighted option.
- Escape closes the palette when mounted in a modal overlay; dismissal is implementation-dependent when embedded inline.
- Input is a
role="combobox" with aria-expanded, aria-controls, and aria-activedescendant.
- List is a
role="listbox"; items are role="option".
- Grouped items announce the group name when arrow navigation crosses group boundaries.
Focus stays in the input while operators type and navigate. Activating an item typically closes the palette and returns focus to the trigger that opened it. If the palette is embedded inline rather than in a modal, ensure the subsequent focus target is explicit—don't leave focus on an unmounted element.
Screen reader announcements
Typical sequence:
- Trigger → "Open command palette, button".
- Palette opens → input focused → "Search actions, combobox, expanded, listbox with 12 options".
- Arrow down → "Create incident, option, 1 of 12".
- Enter → palette closes → flow continues in the new view.
- Very large lists. Arrow navigation through thousands of items is slow for assistive tech. Prefer to filter aggressively and cap visible results.
- Recent items. If recents surface ahead of a full list, label the group ("Recent") so the source of the first few options is clear.
- Custom renderers. Rich item content (icons, counts, shortcuts) must not hide the accessible name. Keep a plain text anchor and annotate with
aria-label when necessary.