Skip to content
Component installs need the registry setup
VegaStack Design

Shortcut Overlay

The ?-triggered dialog listing keyboard shortcuts, rendered from a declaration registry — declare once with a category, never hand-list.

Status
stable
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-overlay

Install with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.

pnpm dlx shadcn@4.21.0 add @vegastack/shortcut-overlay

Run 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

BehaviourWhere it lives
Executing the shortcutsThe app's key handler or command layer
Rebinding / customisationApp policy — deliberately no persistence here
Inline hintsKbd 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 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

PropTypeDefaultDescription
allShortcutsLabelstring"All shortcuts"The switch that reveals the other pages' groups.
descriptionReact.ReactNode"Shortcuts available on this page." ("All keyboard shortcuts." while "All shortcuts" is on)The line under the title.
footerReact.ReactNode—Content rendered below the shortcut list, outside the scroll area — a hint or a link to the full shortcuts guide.
groupsreadonly 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.
openboolean—Controlled open state. Pair with onOpenChange; omit to let the built-in <kbd>?</kbd> binding manage it.
searchableboolean—Show the filter input. On by default for more than ten shortcuts.
shortcutsreadonly 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).
titlestring"Keyboard shortcuts"Dialog title.
triggerKeystring | 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

AttributeValues
data-elsewhere""
data-slot"shortcut-overlay" | "shortcut-overlay-category" | "shortcut-overlay-empty" | "shortcut-overlay-footer" | "shortcut-overlay-row" | "shortcut-overlay-scope"

Shortcut definition

PropTypeDefaultDescription
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").
whenboolean—Exclude this shortcut when false — for bindings gated by permission or context. Omitted (undefined) includes it.

Shortcut group

PropTypeDefaultDescription
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").
activeboolean—Where the group applies — true this page (first), omitted everywhere, false elsewhere (shown only with "All shortcuts").
PropTypeDefaultDescription
keys*readonly string[]—Keys as rendered, mac-first (["⌘", "K"]), as on ShortcutDefinition.
label*string—What the shortcut does — verb + noun ("Delegate task").
whenboolean—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 shouldHandle while another overlay owns the keyboard. The editable-target check is isEditableTarget from use-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 labelled Switch; turning it on adds the other pages' groups after the ones already shown, so nothing moves under the reader.
KeyAction
?Open the overlay (outside text fields).
EscapeClose it.
ContractStates tested
Behaviourclosed, open, filtered, empty-filter, gated-when, suppressed, page-scoped, all-shortcuts
Accessibilitykeyboard, labeled, semantic-html
Visualdefault, empty, dark

Do / Don't

Do
Keep one shortcut registry and feed the overlay, tooltips, and Command rows from it.
Don't
Hand-list shortcuts in dialog markup — it goes stale the day someone adds a binding.

On this page