Documentation below is split by multiSelect. Default single-select follows the combobox + listbox pattern with typeahead. multiSelect keeps focus on the inline search input and moves a highlight through option rows via aria-activedescendant.
Single-select mode (default)
What the component handles
Combobox semantics. Input is role="combobox" with aria-expanded, aria-controls pointing at the listbox, and aria-activedescendant reflecting the highlighted option.
Listbox semantics. Results render with role="listbox"; each option has role="option" and reflects aria-selected.
Keyboard navigation. Arrow keys move through results without removing focus from the input.
Selection. Enter activates the highlighted option; value and input text sync per configuration.
What you must provide
Label. Always pass a visible label (trigger / selected-value text). For form fields that need a required asterisk, compose an external Label with required and set Combobox required for aria-required—do not expect an asterisk on the trigger itself.
Status messages. Announce result counts in a polite live region when the filter changes ("12 results", "No results found").
Empty state. Zero results should show explanatory text, not a silent empty listbox.
Async loading. Indicate loading without trapping focus (aria-busy on the list region when appropriate).
Keyboard behavior
Type filters results.
Arrow Down / Up moves the highlighted option; Home / End jump to first / last.
Enter selects the highlighted option.
Escape clears the input or closes the menu depending on state.
Tab leaves the combobox and closes the menu.
When searchable={false} there is no search input. Opening the popover moves focus into the cmdk root (not the Radix Popover wrapper) so Arrow keys, Enter, and Escape work. Option rows stay tabIndex={-1} so Tab still moves between triggers rather than through items.
Focus management
Focus stays in the input while browsing searchable single-select results; activating an option closes the listbox and keeps focus there unless configuration moves focus elsewhere. Without a search field, focus moves to the command list on open so keyboard navigation is available immediately.
Screen reader announcements
Typical sequence:
Input focused → field name and combobox role announced.
Type to filter → polite region announces result count.
Arrow down → option position ("Spanish, option, 1 of 3").
Enter → selection confirmed.
Multi-select mode (multiSelect)
Listbox semantics. Options render as role="option" with aria-selected; selected rows also show a trailing checkbox for sighted users. Keep labels short and unique so screen reader users can scan the list efficiently.
Nested row actions. The trailing checkbox and optional info control sit in an aria-hidden actions region and are not tab stops (tabIndex={-1}). Selection for assistive tech is aria-selected on the option—do not expose a second checkbox widget in the accessibility tree.
Option tooltips. Optional options[].tooltip is exposed to screen readers through aria-describedby (visually hidden description). The info icon remains a pointer/hover affordance for the visual tooltip. The content renders twice (visible tooltip + hidden description), so pass plain strings or id-less nodes — a node carrying its own id would be duplicated in the DOM.
Chips field / input. The inline search input carries role="combobox" with aria-expanded, aria-controls, aria-activedescendant, and aria-autocomplete="list". Pills supplement but must not replace the accessible name of the control.
Keyboard. Arrow Down / Up (and Home / End) move the highlight while focus stays in the input; Home / End skip disabled options; Enter or Space (when the search input is empty) toggles the highlighted option without closing; Escape closes the popover and keeps focus on the input; Backspace or Delete on an empty search input removes the most recently selected value first (collapsed +N more entries before pills still visible in the field). Option rows are not Tab stops — focus stays on the search input with aria-activedescendant. Tab can move into the Clear / Close footer buttons; Clear resets selection without closing, Close dismisses the panel. Storybook MultiSelectKeyboardNavigation demos this full pattern.
Badge overflow. Selected pills wrap with inline search and the field grows vertically for up to three rows; when they overflow that limit, a +N more control exposes the hidden values in a checkbox Menu. The overflow trigger has an accessible button name (for example "+3 more"). Overflow rows are menuitemcheckbox widgets with a decorative check indicator (not a nested interactive checkbox). The overflow menu and the Combobox options popover are mutually exclusive—opening one closes the other.
Overflow menu keyboard. Arrow keys move through checkbox items; Space/Enter toggles checked state without dismissing the menu; Escape closes the overflow menu. If recalculation removes overflow while the menu is open, the menu closes and focus returns to the combobox search input.
Announcements. When selection counts change materially, polite updates ("3 selected") help; debounce so rapid toggles do not stutter assistive tech.
Cross-reference. For heterogeneous checkbox menus, see Menu accessibility; keep Combobox field label plus pill summary as the product contract for this component.
Semantics and roles (single-select)
Input exposes aria-autocomplete (list or both) depending on configuration.
Listbox is hidden from the accessibility tree when collapsed.
Selected option uses aria-selected, not visual-only treatment.
Known caveats
Debouncing. Announce counts on a debounce so every keystroke does not stutter assistive tech.
Large result sets. Cap visible results when needed; communicate truncation if operators must refine search; surface totals in polite regions when virtualized.
Custom item rendering. Rich option content must keep the accessible name short; supplementary text can use aria-describedby.
Mode coverage. Regression-test both modes; listbox assumptions do not cover multiSelect.