# 6460 MINCommand Palette
# 64
60 MIN
Overview
Build a keyboard-driven command palette (Cmd/Ctrl+K) with fuzzy filtering, roving highlight navigation, a focus trap, and proper combobox/listbox ARIA semantics.
Requirements
- The palette is hidden on first render.
- Clicking the trigger (
data-testid="open-palette") opens the palette. - Pressing Cmd/Ctrl+K opens the palette, and pressing it again closes it.
- Pressing Escape closes the palette.
- Clicking the backdrop (
data-testid="backdrop") closes the palette, while clicking inside the panel keeps it open. - When the palette opens, the search input (
data-testid="command-input") receives focus and is empty. - The palette lists every command.
- Typing filters the commands by fuzzy match: every character of the query must appear in the label in order, ignoring case.
- When nothing matches, show
data-testid="no-results"with the textNo results for "<query>". - Clearing the query restores the full list.
- The first command is highlighted when the palette opens and whenever the query changes.
- ArrowDown moves the highlight to the next command, wrapping from the last one to the first.
- ArrowUp moves the highlight to the previous command, wrapping from the first one to the last.
- Enter executes the highlighted command and closes the palette.
- Clicking a command executes it and closes the palette.
- Executing a command sets the
Last actiontext (data-testid="last-action") to that command's label. - While the palette is open, focus stays inside it when pressing Tab.
Notes
App.tsxalready renders the shell (CommandTrigger,Last action, backdrop, and panel) and uses the read-only components. Add the palette logic there —data.tsandComponents.tsxare read-only.CommandTriggertakesonClick;CommandInputtakesvalue,onChange,onKeyDown, and optionalinputRef;CommandListtakescommands,activeIndex,query, andonSelect;CommandFootertakes no props.- Keep the
data-testids that live inApp.tsx:last-actionandbackdrop. - Open/close from a single
windowkeydownlistener (Cmd/Ctrl+K and Escape) and callpreventDefault(). - Filter by subsequence, not
includes("gch"→ "Go to Challenges"). ResetactiveIndexwhen the query changes and wrap with% results.length. - Focus the input on open and keep focus trapped inside the panel (listen for
focusin).
Tests
- keeps the palette hidden on first render
- opens the palette when the trigger is clicked
- opens the palette with Cmd/Ctrl+K
- focuses the search input when opened
- closes the palette when the shortcut is pressed again
- closes the palette on Escape
- closes the palette when the backdrop is clicked
- keeps the palette open when clicking inside the panel
- renders every command
- filters commands as the user types
- matches non-contiguous characters
- is case-insensitive
- shows a no-results message when nothing matches
- restores the full list when the query is cleared
- clears the query when the palette is reopened
- highlights the first command by default
- moves the highlight down with ArrowDown
- wraps from the first to the last with ArrowUp
- wraps from the last to the first with ArrowDown
- resets the highlight when the query changes
- executes the highlighted command with Enter
- executes a command when clicked
- traps focus inside the palette on Tab