Skip to the page
TACTILE UI

Kit part · Dialogs · Search bars

Command palette

Every action and page behind one key: a modal plate whose entry line is a lit display, with the results printed below and the chosen one pressed in.

Element
<dialog>
Needs
kit.css + kit.mjs
Rules
command.rules.md · 5
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

DashboardGD
BatteryGB
Tariffs
In a page · live
GBGo to BatteryCtrlKOpen the palette?Shortcuts
Shortcuts · the keys to press
0 matches
Nothing matchesNo line has “solar diverter”. Try a page, a device or a verb.
Nothing found
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartJust openedTyped, moved downNothing found
Palette
9
DashboardGD
CircuitsGC
2
BatteryGB
Set reserve
0
Nothing matchesNo line has “solar”.

Graphite

PartJust openedTyped, moved downNothing found
Palette
9
DashboardGD
CircuitsGC
2
BatteryGB
Set reserve
0
Nothing matchesNo line has “solar”.
03

API

NameKindValuesWhat it does
.tui-commandclasson <dialog>The palette: a modal plate up to 600px wide, 12% down the window.
data-command-keyattributea letterOpens it with ⌘ or Ctrl and that letter. Leave it off to open only from a key.
data-command-openattributethe dialog’s idOn any key: opens the palette with an empty field.
.tui-command__displayclasson <div>The entry line: a VFD window, a › prompt, the query, the count of matches.
.tui-command__inputclassrole="combobox"The field. aria-expanded, aria-controls and aria-autocomplete="list"; the part sets aria-activedescendant.
.tui-command__countclasson <output>Rewritten as “9 matches”.
.tui-command__list · .tui-command__groupclassrole="listbox" · role="group"The results, in named groups; a group with no match is hidden.
.tui-command__headingclassaria-hiddenThe printed group name (the group’s aria-label says it).
.tui-command__item · role="option"class · attribute—A line: icon, words, <small> detail, shortcut. aria-selected marks the chosen one.
data-value · data-keywordsattributeon an optionWhat the line returns; other words it should be found by.
.tui-command__keys > kbdclass · element—A shortcut drawn as small keys on a skirt.
.tui-command__empty · [data-command-query]class · attributehiddenShown when nothing matches, with the query written into it as text.
.tui-command__footclass—The printed key hints along the bottom.
.tui-command--inlineclasson a <div>The same palette in the page, for a filterable list.
openCommand(d) · filterCommand(d) · commandMatches(text, query)functionkit.mjsOpen it; refilter after changing the lines; the pure match rule.
tui-commandeventdetail: value, optionFired on the dialog when a line runs; then it closes with returnValue = value.
↑ ↓ · Enter · Escapekeyboardin the fieldMove the chosen line (wrapping), run it, close. Focus returns to where it was.
04

Markup

<button class="tui-key" data-command-open="cmd">Commands</button>

<dialog class="tui-command" id="cmd" aria-label="Commands" data-command-key="k">
  <div class="tui-command__display">
    <input class="tui-command__input" type="search" role="combobox" aria-label="Command"
           aria-expanded="true" aria-controls="cmd-list" aria-autocomplete="list"
           autocomplete="off" spellcheck="false" placeholder="Type a command or a page">
    <output class="tui-command__count"></output>
  </div>
  <div class="tui-command__list" id="cmd-list" role="listbox" aria-label="Commands">
    <div class="tui-command__group" role="group" aria-label="Go to">
      <div class="tui-command__heading" aria-hidden="true">Go to</div>
      <div class="tui-command__item" role="option" data-value="battery" data-keywords="storage">
        <svg …></svg><span>Battery</span>
        <span class="tui-command__keys"><kbd>G</kbd><kbd>B</kbd></span>
      </div>
    </div>
  </div>
  <div class="tui-command__empty" hidden>
    <span class="tui-status"><span class="tui-lamp"></span>Nothing matches</span>
    <span>No line has “<span data-command-query></span>”. Try a page, a device or a verb.</span>
  </div>
</dialog>
import '/kit/kit.mjs';   // wires every .tui-command and [data-command-open]

const palette = document.getElementById('cmd');
palette.addEventListener('tui-command', (e) => run(e.detail.value));

import { openCommand } from '/kit/kit.mjs';
openCommand(palette);

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • A modal <dialog> opened with showModal(): focus is held inside and returns to where it was on close.
  • The WAI-ARIA combobox with a listbox: focus stays in the field while aria-activedescendant moves, so typing never stops working.
  • Groups are named with aria-label; the printed headings are aria-hidden so each is read once.
  • The count of matches and “Nothing matches” are text, and the query is written in as text, never as markup.
  • The glowing entry line is display cyan-white on near-black: over 16:1.
06

How it’s built

A native dialog with the kit’s rise. The entry line is a VFD (the display background, scanlines, an inset shadow and a two-step glow on the typed text) with the caret in the same light. command.mjs filters by delegation: every word typed must appear in a line or its keywords, empty groups hide, the first match is chosen. The chosen line is the kit’s recess; shortcuts are drawn as 20px caps on a 1.5px skirt, the same build as a key.

07

Rules

command.rules.mdIn the brain

Lines are named for what they do, grouped by kind, and show their shortcut as the keys to press; finding nothing says what to try instead.

5 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08

Confirm

Erase reel 3?

KeepErase

Dialog

A decision that must be made now: a native dialog with a question, one sentence of consequence, a way back and the act.

96.4MHz

VFD

A vacuum-fluorescent display: cyan-white light on near-black glass, with a glow and a ghost. Frequencies, levels, status screens.

Menu

A card of actions and options that slides out from under its key. The key stays down while the card is out; an option that is on lights its lamp.

Docs
Finish
Volume