Skip to content
Devix Open Source

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-motion drops the entry transition and the loading pulse.
  • forced-colors draws 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