Shortcut Overlay
The ?-triggered dialog listing keyboard shortcuts, rendered from a declaration registry — declare once with a category, never hand-list.
- Status
- Since
0.4.0- Accessibility pattern
- description list in a dialog
Last updated
Or press ? anywhere outside a text field.
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Shortcut Overlay before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify shortcut-overlayInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/shortcut-overlayRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
The same command installs the registry items it composes: @vegastack/dialog, @vegastack/kbd, @vegastack/label, @vegastack/panel-search, @vegastack/scroll-area, @vegastack/switch, @vegastack/use-platform.
Usage
import {
ShortcutOverlay,
type ShortcutDefinition,
} from "@/components/ui/shortcut-overlay";
const SHORTCUTS: ShortcutDefinition[] = [
{ keys: ["⌘", "K"], label: "Open command menu", category: "Navigation" },
{ keys: ["E"], label: "Edit selected record", category: "Editing" },
];
<ShortcutOverlay shortcuts={SHORTCUTS} />;The value is the registry model, not the dialog: a hand-listed shortcuts
dialog goes stale the day someone adds a binding. Declare each shortcut once —
keys, label, category, an optional when — and the overlay renders from data.
The same declarations can feed tooltip hints and
Command rows.
Keys are mac-first. Kbd is upstream's presentational <kbd> and rewrites
nothing, so the overlay resolves modifier labels itself through
use-platform — the command glyph renders as Ctrl for
non-mac users automatically.
Scope
| Behaviour | Where it lives |
|---|---|
| Executing the shortcuts | The app's key handler or command layer |
| Rebinding / customisation | App policy — deliberately no persistence here |
| Inline hints | Kbd inside a Tooltip / CommandShortcut, same data |
Examples
Large sets get a filter
The filter appears automatically past ten shortcuts (or force it with
searchable). It is the shared panel-search row — a
shared internal this overlay and EmojiPicker both install, so the two
popups cannot drift into two search rows.
Page-scoped groups
Pass groups instead of shortcuts when the keys depend on the page. Each group says where it
applies through active: true is this page's own keys and comes first, an omitted active is a
group that works everywhere ("General", "Go to") and follows, and false is another page's group,
hidden until the reader turns on All shortcuts — which starts off at every opening. A filter
query searches every group, hidden ones included.
Footer
footer renders below the list, outside the scroll area, under a hairline — a hint or a link to a
fuller shortcuts guide.
<ShortcutOverlay
shortcuts={SHORTCUTS}
footer={<a href="/help/shortcuts">All shortcuts</a>}
/>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
allShortcutsLabel | string | "All shortcuts" | The switch that reveals the other pages' groups. |
description | React.ReactNode | "Shortcuts available on this page." ("All keyboard shortcuts." while "All shortcuts" is on) | The line under the title. |
footer | React.ReactNode | — | Content rendered below the shortcut list, outside the scroll area — a hint or a link to the full shortcuts guide. |
groups | readonly ShortcutGroup[] | — | Page-scoped sections: the active page's groups first, then the groups that apply everywhere,
and the other pages' groups (active: false) behind an "All shortcuts" switch, which resets
each time the overlay opens. A filter query searches every group, shown or not. |
onOpenChange | ((open: boolean) => void) | — | Fired when the overlay wants to open or close. |
open | boolean | — | Controlled open state. Pair with onOpenChange; omit to let the built-in
<kbd>?</kbd> binding manage it. |
searchable | boolean | — | Show the filter input. On by default for more than ten shortcuts. |
shortcuts | readonly ShortcutDefinition[] | [] | The declared shortcuts, sectioned by category in first-appearance order. Pass this or
groups; with groups it is ignored. |
shouldHandle | (() => boolean) | — | Predicate consulted before the trigger key opens the overlay — return
false while another overlay owns the keyboard (the same suppression rule
useListNav uses). |
title | string | "Keyboard shortcuts" | Dialog title. |
triggerKey | string | false | "?" | Bind the global open key. false disables the built-in binding (the host
opens the overlay itself). The binding never fires while focus is in a
text field or another editable surface. |
Data attributes and CSS variables on ShortcutOverlay
| Attribute | Values |
|---|---|
data-elsewhere | "" |
data-slot | "shortcut-overlay" | "shortcut-overlay-category" | "shortcut-overlay-empty" | "shortcut-overlay-footer" | "shortcut-overlay-row" | "shortcut-overlay-scope" |
Shortcut definition
| Prop | Type | Default | Description |
|---|---|---|---|
category* | string | — | Section the shortcut is grouped under ("Navigation", "Editing"). |
keys* | readonly string[] | — | Keys as rendered, mac-first (["⌘", "K"]). Kbd's os="other" rewriting
translates the modifier glyphs for non-mac platforms automatically. |
label* | string | — | What the shortcut does — verb + noun ("Open command menu"). |
when | boolean | — | Exclude this shortcut when false — for bindings gated by permission or
context. Omitted (undefined) includes it. |
Shortcut group
| Prop | Type | Default | Description |
|---|---|---|---|
id* | string | — | Stable id (React key). |
items* | readonly ShortcutGroupItem[] | — | The group's shortcuts, in display order. |
label* | string | — | The section heading ("This page", "General", "Go to"). |
active | boolean | — | Where the group applies — true this page (first), omitted everywhere, false elsewhere
(shown only with "All shortcuts"). |
| Prop | Type | Default | Description |
|---|---|---|---|
keys* | readonly string[] | — | Keys as rendered, mac-first (["⌘", "K"]), as on ShortcutDefinition. |
label* | string | — | What the shortcut does — verb + noun ("Delegate task"). |
when | boolean | — | Exclude this shortcut when false. Omitted includes it. |
Accessibility
- Shortcuts render as a description list, so each label/keys pair is announced
as a pair; the keys are real
<kbd>elements. - The ? binding never fires while focus is in an input, textarea,
select, or contenteditable surface, and defers to
shouldHandlewhile another overlay owns the keyboard. The editable-target check isisEditableTargetfromuse-platform, the same guard the sidebar's ⌘/Ctrl + B uses, so a global shortcut of your own can reuse it. - The dialog is labelled and closes on Escape; category headers are real headings.
- With
groups, All shortcuts is a labelledSwitch; turning it on adds the other pages' groups after the ones already shown, so nothing moves under the reader.
| Key | Action |
|---|---|
| ? | Open the overlay (outside text fields). |
| Escape | Close it. |
| Contract | States tested |
|---|---|
| Behaviour | closed, open, filtered, empty-filter, gated-when, suppressed, page-scoped, all-shortcuts |
| Accessibility | keyboard, labeled, semantic-html |
| Visual | default, empty, dark |