Command Menu
How the Command Menu works with a keyboard and assistive tech, and where it stands against WCAG 2.2 AA.
The Command Menu follows the WAI-ARIA combobox pattern: focus stays in the search field, the arrow keys move a highlight through a listbox of results, and aria-activedescendant tells the screen reader which result is current. One result is always highlighted, so Enter always has a destination.
Keyboard#
Highlights the next result, passing over group labels. Wraps from the last to the first.
Highlights the previous result. Wraps from the first to the last.
Runs the highlighted result.
Clears the query and the filter. With nothing to clear, closes the menu.
Leaves the field for the clear button, then the filter tags. Results are not tab stops.
Opens the menu. The host binds this shortcut; the component does not.
Semantics#
role="combobox"With aria-autocomplete="list", aria-controls on the results and aria-expanded="true", since the list is always shown.
Search fieldaria-activedescendantPoints at the highlighted option, so it is announced while focus stays in the field.
A result is highlightedrole="listbox"Named Results. Each group inside is role="group", labelled by its heading.
Resultsrole="option" aria-selectedaria-selected="true" on the highlighted row only.
Each resultaria-pressedInside a group named Filter results. The chosen tag is pressed.
Filter tagsaria-live="polite"A hidden line says how many results there are after every change.
Result countaria-hidden="true"The key hints repeat what the keyboard already does, so they are hidden.
Footer<dialog>Shown with showModal(), so the page behind is inert. Give the dialog an aria-label (Command menu); the preview does not.
Opened as a dialogColour contrast#
Every variant and state has to meet WCAG 2.2 Level AA. Measure the colours as they land on screen: a translucent hover or press wash over whatever sits behind it.
Result text · Rest, Highlighted
14px titles (#2a2a2a) and descriptions (#6d6d6d) against white and the 4% highlight wash.
WCAG 1.4.3 Contrast (Minimum) ↗Field, tags and footer · Always
Group labels and unchosen tags are #6d6d6d on white (5.17:1); a chosen tag is white on #2a2a2a (14.35:1); footer hints are #6d6d6d on #f2f2f2 (4.62:1). The #8f8f8f placeholder measures 3.23:1, which is allowed only because the field also has an aria-label.
WCAG 1.4.3 Contrast (Minimum) ↗Highlighted row · Highlighted
The highlight is the keyboard's focus indicator here, and its 4% wash measures 1.09:1 against white. It does not show reliably.
WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.7 Focus Visible ↗Focus indicator · Field, clear, tags
The field, clear button and tags ring in #1a6ef8 (4.54:1 on white), with a white gap on the field and tags.
WCAG 1.4.11 Non-text Contrast ↗WCAG 2.4.13 Focus Appearance ↗Measured contrast#
Text in a result row against the row, at rest and highlighted. The shortcut key and badge carry their own fills, so the highlight does not reach them. 2 of 8 pairs fall below their minimum (4.5:1 for text, 3:1 for graphics) and are marked in amber.
Motion#
- Row, tag and field colour changes take 100–120ms and are turned off under prefers-reduced-motion.
- Opened as a dialog it rises 6px and fades over 200ms only when motion is allowed.
- Moving the highlight scrolls the list to the nearest edge, without smooth scrolling.
Checklist#
The highlighted row is a 4% wash, about 1.1:1 against white. Give it a stronger fill or an outline before the code library ships; it is the only sign of where the keyboard is.
The badge (#229a14 on #f4fef2) is below 4.5:1: see the table.
When it opens as a dialog, name the dialog and put focus in the search field. Closing it should return focus to whatever opened it.
Bind ⌘K (and Ctrl K) in the host, and make sure it does not override a shortcut the page or the screen reader relies on.
Keep result titles unique enough to tell apart when read alone: Roadmap Q4, not Roadmap twice.
Rows are 48 or 64px and tags 26px, all over the 24px target minimum. The clear button is 26 × 30px.
