Guide
Accessibility
The palette is a native <dialog> opened with showModal(), with the WAI-ARIA combobox pattern
inside it.
Roles
| Element | Role | Carries |
|---|---|---|
| The dialog | dialog (modal) |
aria-label |
| The field | combobox |
aria-expanded, aria-controls, aria-autocomplete="list", aria-activedescendant |
| The list | listbox |
aria-label |
| Each row | option |
aria-selected, aria-disabled |
| A live region | status |
"6 commands", "Searching…" |
Focus stays in the field the whole time — moving through the list is aria-activedescendant, which is
what a screen reader expects from a combobox and what keeps typing working.
Keyboard
| Key | Does |
|---|---|
| ⌘K / CtrlK | Open or close. |
| ↓ ↑ | Move, wrapping at the ends, skipping disabled commands. |
| Home / End | First and last, when the field is empty. |
| PageDown / PageUp | Eight at a time. |
| ↵ | Run the highlighted command. |
| Backspace | With the field empty, steps back out of a page. |
| ? | With the field empty, opens the cheat sheet. |
| Esc | Leaves the page, then closes the palette. |
What the browser gives us
Focus is trapped inside the dialog, the rest of the page is inert for screen readers as well as pointers, Escape is handled, it renders in the top layer above every stacking context, and focus returns to whatever opened it.
Time and motion
- Results are announced through a polite live region, so a screen reader hears "12 commands" rather than each row.
prefers-reduced-motiondrops the entry transition and the loading pulse.forced-colorsdraws real borders and uses system colours for the active row and highlights.- On a small screen the palette fills it, rows grow to 52px, and the hint bar steps aside for the keyboard.
Shortcuts that stay out of the way
A bare letter shortcut never fires while the focus is in an input, a textarea or a contenteditable — the thing that makes ninja-keys' #35 so annoying. Shortcuts with a modifier still work everywhere, including inside the palette's own field.
Updated 12 Sep 2026