Command
Finds and runs a command by typing.
Placement
inline
dialogdefault
States
rest
searching
empty
loading
Usage
When to use it
- Use
- The palette: every action and place in the tool, found by typing, opened with mod+k.
- Use
- A long list of actions on one page, filtered as the person types.
- Not for
- A few actions on one thing. Use Dropdown menu.
- Not for
- The person picks a value for a field. Use Combobox.
Rules
7 rules
- Must
- Give Command a label that says what it searches. Because it names the search field and the list for a screen reader, which sees neither the placeholder nor the headings while typing.
- Must
- Write each item as a verb and its object in sentence case; the row sets the capitals. Because an item is an action, and the matched letters are drawn from the words as written.
- Should
- Add keywords for the other words people use: "rerun" and "retry" for Replay. Because a keyword match ranks last but still finds the item.
- Must
- Set shouldFilter={false} and mount CommandLoading while results come from a server. Because the list would filter the server's answer a second time, and the pending mark says the search is still going.
- Must
- Keep CommandDialog's hidden title, or pass your own DialogTitle. Because the dialog is named by its title, and without one a screen reader announces only "dialog".
- Never
- Box CommandInput in a field or put a frame around a group. Because the search is the list's first ruled row, and a box inside the box is a second edge.
- Never
- Pad a CommandItem, a group heading or a shortcut in className. Because every row, the search, the headings, the items and their key caps, starts and ends on the 12px --inset, and 40px rows put each 16px icon 12px from its top and from the side, right under the search glyph.
State order
4 states, highest first
- 1
- loading
- 2
- empty
- 3
- searching
- 4
- rest
Accessibility
4 checks
- The input is a combobox pointing at the active option with aria-activedescendant; focus never leaves it.
- A polite status says how many results there are, and which are unavailable.
- The active row lifts to white off the panel between two --line hairlines, text-1 on it 18.3:1 and 13:1; that lift is the focus indicator, and forced colors draw a 2px outline.
- Each option carries its place in the ranked order (aria-posinset).
Keyboard
4 keys
- Up, Down
- Moves the active item; Ctrl+P and Ctrl+N too.
- Home, End
- First and last item, when the field is empty or with Ctrl.
- Enter
- Runs the active item.
- Escape
- Closes the palette and returns focus to what opened it.
Edge cases
5 edge cases
- Nothing matches
- A sentence and the query take the list's place.
- Whitespace typed
- No search: every group shows as written.
- Slow results
- The pending mark takes the search icon's place, so the text never moves.
- Touch
- The key hints hide; a tap runs an item.
- Phone, 390 wide
- The palette leaves 16px of page each side.
API
8 props
Command labelstringdefault'Commands'Command shouldFilterbooleandefaulttrueCommand filter(value, search, keywords) => numberdefaultrank by matchCommand loopbooleandefaultfalseCommandDialog title, descriptionReactNode | nulldefaulthiddenCommandItem value, keywordsstring, string[]defaultits textCommandItem onSelect(value) => voidCommandShortcut, CommandHint keysstring
Tokens
18 tokens
--panel- #fafaf7
--page- #ffffff
--line- #e2dfd5
--row-highlight- #ffffff
--row-highlight-edge- var(--line)
--inset- 12px
--row- 40px
--row-sm- 32px
--popup-max- 320px
--type-body-sm- 400 14px/20px var(--font-sans)
--type-label- 400 13px/16px var(--font-mono)
--type-micro- 400 11px/14px var(--font-mono)
--text-1- #161512
--text-2- #55524a
--text-3- #6b675e
--pending-cell- 3px
--overlay-lg- 640px
--z-overlay- 50