Problem
The search page's author/channel facet pickers (resources/js/pages/channels/Search.vue, introduced in #395) are implemented as a DropdownMenu with a filter <Input> inside it. This is functional and keyboard-operable (Tab between the filter and the option buttons, Enter to select, Escape to close, type-to-filter) and the page passes the axe audit in both themes with the pickers closed — but it is not a proper combobox pattern:
- The filter input uses
@keydown.stop to keep the menu's type-ahead from hijacking typing, which also blocks arrow-key roving to the options.
- The option rows are plain
<Button>s inside DropdownMenuContent rather than registered menu/listbox items, so there is no roving aria-activedescendant navigation.
- A text input inside a
menu role is an ARIA anti-pattern; the open state is not axe-audited.
Surfaced by a local CodeRabbit review on the #391 search-redesign PR; deferred to keep that (already large) PR scoped.
Acceptance criteria
- The author and channel facet pickers expose proper combobox semantics (a
role="combobox" input with aria-expanded/aria-controls, a listbox of options, roving aria-activedescendant or focus), reusing/introducing a shared primitive (reka-ui Combobox/Listbox or the existing command component) rather than a DropdownMenu.
- Full keyboard flow: open, type to filter, arrow to navigate, Enter to select, Escape to dismiss.
- Filtering + single-select behavior preserved;
data-test hooks preserved so the existing browser tests keep passing.
- An axe audit runs against the open picker in a Pest browser test, in light and dark themes.
Problem
The search page's author/channel facet pickers (
resources/js/pages/channels/Search.vue, introduced in #395) are implemented as aDropdownMenuwith a filter<Input>inside it. This is functional and keyboard-operable (Tab between the filter and the option buttons, Enter to select, Escape to close, type-to-filter) and the page passes the axe audit in both themes with the pickers closed — but it is not a proper combobox pattern:@keydown.stopto keep the menu's type-ahead from hijacking typing, which also blocks arrow-key roving to the options.<Button>s insideDropdownMenuContentrather than registered menu/listbox items, so there is no rovingaria-activedescendantnavigation.menurole is an ARIA anti-pattern; the open state is not axe-audited.Surfaced by a local CodeRabbit review on the #391 search-redesign PR; deferred to keep that (already large) PR scoped.
Acceptance criteria
role="combobox"input witharia-expanded/aria-controls, alistboxofoptions, rovingaria-activedescendantor focus), reusing/introducing a shared primitive (reka-uiCombobox/Listboxor the existingcommandcomponent) rather than aDropdownMenu.data-testhooks preserved so the existing browser tests keep passing.