Command palette
The single ⌘K surface of the app: search, commands and page actions in one overlay. React <GlobalSearch> (apps/web/src/components/GlobalSearch.tsx) plus apps/web/src/components/search/*; CSS apps/web/src/ds/global-search.css. It reuses the Dialog shell (.acrm-dialog-scrim + .acrm-dialog) and draws every result through the canonical Item list — the palette owns no row of its own.
Approved by the product owner on 7 September 2026 as deployed on dev. The code is the source of truth; the E1 wireframe that this was built from (:3777/preview/hledani-v2-palette.html) is history and is marked as superseded.
When to use
- Getting to a record from anywhere: people, companies, deals — one keystroke, no navigation.
- Running a command without leaving the current screen ("Nová osoba", "Naplánovat schůzku").
- Acting on the record under the cursor — the right pane carries its facts and its actions.
- Reaching a page by name instead of by clicking through the navigation.
When not to use
- Filtering a list that is already on screen — that is Filter bar, not the palette.
- Picking a related record inside a form or a dialog — that is Record select.
- A hover glance at an entity — that is Entity preview popover; the palette is for deliberate work, the popover for a glance.
- Bulk work over a chosen set — the palette offers bulk actions over an existing selection, it does not build the selection.
Anatomy
- Shell: Dialog scrim (
z-index: 90) +.acrm-dialog, widthmin(800px, 94vw)— the "800 palette" role of the dialog ladder (UI-RULES §10). Gridauto 1fr auto: query row, body, footer. - Query row
.gsp-q: height 52px, bottom hairline, glass glyph, input400 15px/1.25 var(--font-sans),Escchip (.acrm-kbd). An active narrowing shows as a token.gsp-prebefore the input — the prefix character itself leaves the field. Since 8 Sep 2026 the token is a blue pill (--accent-tint/--accent-ink, 22px) carrying the prefix glyph plus its label; the glyph takes no explicit size (1em fromfont: var(--small)) and the pill's left padding is one step smaller than the right, because the Tabler drawing already carries an inset. - Body
.gsp-body: fixedheight: 460px, flex column. On desktop it holdsSplitView(.gsp2,asideWidth="minmax(0, 220px)",divider="line") — results left, detail of the row under the cursor right. Each column scrolls on its own. - Results: canonical Item list,
.il .il-sm .il-embedded .il-seamless— row height--row-h-nav(28px), no dividers between rows (the hairline belongs to the query row only). Lead is an avatar or a type glyph and.il-tthe name — nothing else in the row (no.il-meta: no "why found" reason, no key hint on the active row; acrm !4144, 29 Sep 2026). Why a record matched is carried by its section ("Podle významu"), by the highlight in the name and by the matched field in the detail pane; the key is shown once, in the action panel ("Otevřít ↵"), and the active row is recognised by its fill. - Section label
.gsp-sec: a list divider, not a page heading —--labelin--fg-secondary, count in--small, sentence case, no frame, no caps. Fixed section order Lidé → Firmy → Obchody; an empty section is not drawn. A failed section carries.errin--critical-inkon its own label. - "dalších N"
.gsp-more: a quiet row in the rhythm of the list that loads more into the section — a continuation of the listing, not a button and not a narrowing. - Locked rows: rows outside the user's scope are found and only marked — drawn statically at the end of their section, name only, masked sub-label, no click. The order arrives from the server and the client never re-sorts it.
- Detail pane
.gsp2-detail: padding and gap--sp-5. Header.gsp2-head(avatar or action glyph +.gsp2-name:.tname,.gap,.kkind — the kind is the LAST WORD of the name, inline in quiet--p--fg-secondary, 8px (--sp-3) after it, so on a multi-line name it ends the last line; the centre of the mark sits on the centre of the FIRST line; the name clamps at 5 lines and on overflow the kind stays visible behind the single ellipsis at the end of line 5 —useClampedName, ACRM-381), facts in the definition grid.gsp2-def(20px minmax(64px, max-content) minmax(0, 1fr), column gap--sp-4) whose icon column and gap match.il-leadand.il-rowexactly, so a fact glyph sits on the same vertical as the action glyph below it. Actions sit at the bottom of the column on a canonical Item list, separated by a hairline. - Match highlight:
<mark>in--accent-tint/--accent-ink, radius--radius-xs— the same recipe the meeting transcript search already uses, in both columns. - Status strip
.gsp-strip:--warning-subtle/--warning-ink. It states what the surface can still do and stays visible even after results arrive (R17). ⚠️ Its CSS header still describes it as sitting "above the footer, outside the body", but since the two-column rework the code renders it insideleftColumn(GlobalSearch.tsx:1400) — i.e. inside the 220px results column, where it wraps onto two lines. Measured, open for the AD (seedocs/REVIEW-command-palette-family.mdA2). - Footer
.gsp-foot: a full-width button, 44px,--smallin--fg-mutedon--surface-75, always visible so nothing jumps after the first character. Carries the query in quotes and the⌘↵chip; it only pre-fills the assistant composer, it never sends. - Empty state: the left column lists recently opened records, "Vytvořit" and "Stránky"; the right column carries the prefix help.
- "Nothing found":
.acrm-emptywith the query in the sentence, and under it.gsp-acts/.gsp-act— two sibling actions ("Založit osobu" / "Založit firmu"), never an edit of.acrm-emptyitself. The actions centre with the sentence (ACRM-161, 8 Sep 2026): they answer it, so they share its axis. Until then.gsp-actshad nojustify-contentand both buttons sat at 0px from the left edge of an 800px dialog.
Prefixes
> was tried for commands and dropped: on a Czech keyboard it needs right-Alt, so it is now ordinary text with no token (palette-model.ts:345). Anything still documenting > is out of date.
Four, and the character never stays in the field — it becomes the .gsp-pre token, so the query reads the same with or without it. Backspace on an empty field removes the token.
| prefix | narrows to |
|---|---|
@ | Lidé — people only |
# | Firmy — companies only |
: | Příkazy — commands and pages |
? | Asistent — a question for the assistant |
Mobile (≤ 720px)
The palette is the screen: fixed inset 0, no scrim, no radius, and it carries its own z-index: 90 because there is no scrim to provide one. SplitView is not mounted at all — the body holds either the list or the detail, never both, and the detail is a second step reached by opening a row, with .gsp-back above it. Esc gives way to a textual "Zrušit" (.gsp-cancel).
Best practices
Do
- Draw every result through Item list; the palette owns the shell and the section label, never a row of its own.
- Keep the footer visible from the moment the palette opens — it is an exit, not a result.
- Let the server decide row order, including where locked rows sit.
- Keep the detail pane's fact grid on the same vertical rhythm as the list rows.
Don't
- Don't put a grey reason ("klíč", "význam", "telefon") or a key hint into a result row — the section, the highlight and the detail pane already say it (no grey explanatory text, ACRM-375–377).
- Don't put the record kind on its own line under the name, and don't let a long name push it out of view — the kind follows the name (ACRM-381).
- No dividers between result rows — use
.il-seamless. A hairline between short rows turns a selection into a table. - No chips on rows; the type is carried by the lead glyph.
- No frame around a section — the panel is already a frame.
- No spinner while loading; a skeleton in the row rhythm keeps the layout from re-flowing.
- No full-screen error when one section fails — the failure belongs to that section's label.
- No empty row: when there is no company or city, the sub-label carries the last activity.
Content
Sentence case everywhere, no caps, no trailing periods on labels. Section labels are plain nouns ("Lidé", "Firmy", "Obchody", "Vytvořit", "Stránky").
Related
- Item list — every row of the palette, including the
.il-seamlessvariant this surface established. - Dialog — the shell, the scrim and the elevation.
- Entity preview popover — the glance counterpart; both take their facts from the same place, so the two may differ in form but never in content.
- Split view — the two-column body, in its "list | reader" variant.
Source: docs/command-palette/command-palette.usage.md · GitLab