Component
Command palette
A searchable list of actions, pages and records, opened over the page with ⌘K.
Plain-text source, for agents and clipboard use: /components/source/command-palette.txt
When to use it
Gets people to any action, page or record by typing a few letters. It is a native dialog holding a combobox and a listbox of grouped options. Focus stays in the input; the arrow keys move the active option and Enter chooses it. An option with data-href navigates; any other dispatches flux-command with its value and closes the palette. It resets when it closes.
Not this
Not as the only way to reach something; every command must also live in the interface. Not for searching within one list; use Search on the list. Not for choosing a form value; use Select.
Examples
Palette
Type “golden” to filter to the records. The arrow keys move, Enter chooses, Escape closes.
html
<button class="flux-button flux-button--secondary" type="button" commandfor="command-palette" command="show-modal">Open command palette</button>
<dialog class="flux-command" id="command-palette" aria-label="Command palette" data-flux-command>
<div class="flux-command__search">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="7" cy="7" r="4" /><path d="M10 10l3.5 3.5" /></svg>
<input class="flux-command__input" type="text" role="combobox" aria-expanded="true" aria-controls="command-palette-list" aria-autocomplete="list" aria-label="Search commands and records" placeholder="Search commands and records" autocomplete="off" autofocus />
</div>
<div class="flux-command__list" id="command-palette-list" role="listbox" aria-label="Results">
<div class="flux-command__group" role="group" aria-labelledby="command-group-0">
<div class="flux-command__group-label" id="command-group-0">Actions</div>
<div class="flux-command__option" id="command-new-booking" role="option" aria-selected="false" data-value="new-booking" data-keywords="create reservation"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3.5v9M3.5 8h9" /></svg><span>New booking</span></div>
<div class="flux-command__option" id="command-new-quote" role="option" aria-selected="false" data-value="new-quote" data-keywords="create proposal"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3.5v9M3.5 8h9" /></svg><span>New quote</span></div>
<div class="flux-command__option" id="command-assign-guide" role="option" aria-selected="false" data-value="assign-guide" data-keywords="staff"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 3.5v9M3.5 8h9" /></svg><span>Assign a guide to a departure</span></div>
</div>
<div class="flux-command__group" role="group" aria-labelledby="command-group-1">
<div class="flux-command__group-label" id="command-group-1">Go to</div>
<div class="flux-command__option" id="command-bookings" role="option" aria-selected="false" data-value="bookings"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 2.5h5.5L12 5v8.5H4z" /><path d="M9.5 2.5V5H12" /></svg><span>Bookings</span></div>
<div class="flux-command__option" id="command-departures" role="option" aria-selected="false" data-value="departures"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 2.5h5.5L12 5v8.5H4z" /><path d="M9.5 2.5V5H12" /></svg><span>Departures</span></div>
<div class="flux-command__option" id="command-suppliers" role="option" aria-selected="false" data-value="suppliers" data-keywords="hotels contracts"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 2.5h5.5L12 5v8.5H4z" /><path d="M9.5 2.5V5H12" /></svg><span>Suppliers</span></div>
<div class="flux-command__option" id="command-reports" role="option" aria-selected="false" data-value="reports"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 2.5h5.5L12 5v8.5H4z" /><path d="M9.5 2.5V5H12" /></svg><span>Reports</span></div>
</div>
<div class="flux-command__group" role="group" aria-labelledby="command-group-2">
<div class="flux-command__group-label" id="command-group-2">Records</div>
<div class="flux-command__option" id="command-kb-104931" role="option" aria-selected="false" data-value="kb-104931" data-keywords="golden circle"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1" /><path d="M5 6.5h6M5 9.5h4" /></svg><span>Anna Lindqvist</span><span class="flux-command__meta">KB-104931</span></div>
<div class="flux-command__option" id="command-kb-104877" role="option" aria-selected="false" data-value="kb-104877" data-keywords="agent"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1" /><path d="M5 6.5h6M5 9.5h4" /></svg><span>Nordic Trails</span><span class="flux-command__meta">KB-104877</span></div>
<div class="flux-command__option" id="command-dep-0914" role="option" aria-selected="false" data-value="dep-0914" data-keywords="departure"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2.5" y="3.5" width="11" height="9" rx="1" /><path d="M5 6.5h6M5 9.5h4" /></svg><span>Golden Circle, 14 Sep</span><span class="flux-command__meta">DEP-0914</span></div>
</div>
</div>
<p class="flux-command__empty" hidden>No commands or records match.</p>
<p class="flux-command__status flux-visually-hidden" role="status"></p>
<div class="flux-command__footer" aria-hidden="true">
<span><kbd class="flux-kbd">↑</kbd><kbd class="flux-kbd">↓</kbd> to move</span>
<span><kbd class="flux-kbd">↵</kbd> to choose</span>
<span><kbd class="flux-kbd">Esc</kbd> to close</span>
</div>
</dialog>Anatomy
| Part | Description |
|---|---|
| Dialog | On surface-overlay with shadow-xl, 40rem wide, set down from the top so it does not jump as results filter. |
| Search | A combobox input at text-base with a search icon, above a hairline. |
| Group | A mono uppercase label and its options. Groups with no matches hide. |
| Option | An icon, the label and optional mono meta such as a reference. The active option is surface-selected. |
| Empty state | One sentence when nothing matches. |
| Footer | The keys that work in the palette. |
API
| Name | Kind | Description |
|---|---|---|
dialog | element | Open with showModal() or a commandfor button. |
flux-command | class | On the dialog. data-flux-command adds filtering, the active option and choosing. Name it with aria-label. |
data-flux-command-shortcut | attribute | Opens the palette on ⌘K or Ctrl K, and on the app shell’s flux-shell-search event. Put it on one palette per page. |
flux-command__search | __input | class | The input row. The input has role="combobox", aria-controls naming the list and aria-expanded="true". |
flux-command__list | class | The role="listbox" element. |
flux-command__group | __group-label | class | A role="group" labelled by its label. |
flux-command__option | __meta | class | A role="option" with a unique id. data-keywords adds search terms; data-href navigates; data-value is sent with flux-command. |
flux-command__empty | __status | __footer | class | The no-results line, the visually hidden result count and the key hints. |
flux-command event | attribute | Event dispatched on the dialog when an option without data-href is chosen. detail holds option and value. |
States
| State | Treatment |
|---|---|
| Open | The first matching option is active. |
| Filtered | Options and groups that do not match every typed word hide; the count is announced. |
| No results | The empty line shows and no option is active. |
| Closed | The query clears so the next open starts fresh. |
Keyboard
| Keys | Action |
|---|---|
⌘K / Ctrl K | Opens the palette, with data-flux-command-shortcut. |
Arrow Down / Arrow Up | Moves the active option, wrapping at the ends. |
Enter | Chooses the active option. |
Escape | Closes the palette and returns focus to where it was. |
Accessibility
- Focus never leaves the input. aria-activedescendant points at the active option, so typing and moving work together.
- The result count is announced through a status region as the list filters.
- showModal() makes the page inert; a click on the backdrop closes the palette.
- If the product already uses ⌘K, as this documentation site does, leave data-flux-command-shortcut off and open the palette from that handler.
Tokens
The tier-3 and tier-2 tokens this component binds to. Change the token, not the component.
- --flux-command-width | -list-height | -offset | -padding | -input-height
- --flux-command-bg | -shadow | -backdrop | -divider
- --flux-command-option-padding-x | -option-padding-y | -option-active | -option-icon | -group-label
Do and don’t
Do
- Name options with the verb first for actions, and the record name for records.
- Match on references and synonyms through data-keywords.
Don’t
- Do not put destructive actions in the palette without a confirmation.
- Do not show more than three groups before the user types; lead with the most used.