Flux 2.0

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.

Actions
New booking
New quote
Assign a guide to a departure
Go to
Bookings
Departures
Suppliers
Reports
Records
Anna LindqvistKB-104931
Nordic TrailsKB-104877
Golden Circle, 14 SepDEP-0914

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

PartDescription
DialogOn surface-overlay with shadow-xl, 40rem wide, set down from the top so it does not jump as results filter.
SearchA combobox input at text-base with a search icon, above a hairline.
GroupA mono uppercase label and its options. Groups with no matches hide.
OptionAn icon, the label and optional mono meta such as a reference. The active option is surface-selected.
Empty stateOne sentence when nothing matches.
FooterThe keys that work in the palette.

API

NameKindDescription
dialogelementOpen with showModal() or a commandfor button.
flux-commandclassOn the dialog. data-flux-command adds filtering, the active option and choosing. Name it with aria-label.
data-flux-command-shortcutattributeOpens 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 | __inputclassThe input row. The input has role="combobox", aria-controls naming the list and aria-expanded="true".
flux-command__listclassThe role="listbox" element.
flux-command__group | __group-labelclassA role="group" labelled by its label.
flux-command__option | __metaclassA 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 | __footerclassThe no-results line, the visually hidden result count and the key hints.
flux-command eventattributeEvent dispatched on the dialog when an option without data-href is chosen. detail holds option and value.

States

StateTreatment
OpenThe first matching option is active.
FilteredOptions and groups that do not match every typed word hide; the count is announced.
No resultsThe empty line shows and no option is active.
ClosedThe query clears so the next open starts fresh.

Keyboard

KeysAction
⌘K / Ctrl KOpens the palette, with data-flux-command-shortcut.
Arrow Down / Arrow UpMoves the active option, wrapping at the ends.
EnterChooses the active option.
EscapeCloses 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.