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
Add Shortcut Overlay from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/shortcut-overlayThe same command installs the registry items it composes: @vegastack/dialog, @vegastack/floating-surface, @vegastack/kbd, @vegastack/scroll-area, @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's modifier rewriting is
driven by use-platform, so the command glyph
renders as Ctrl for non-mac users automatically (and mac glyphs
carry spoken-name text for screen readers).
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 | TooltipKbd / CommandShortcut, fed by the same data |
Examples
Large sets get a filter
The filter appears automatically past ten shortcuts (or force it with
searchable).
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
shortcuts* | readonly ShortcutDefinition[] | — | The declared shortcuts. Categories render in first-appearance order. |
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. |
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-slot | "shortcut-overlay" | "shortcut-overlay-category" | "shortcut-overlay-empty" | "shortcut-overlay-row" |
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. |
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 dialog is labelled and closes on Escape; category headers are real headings.
| Key | Action |
|---|---|
| ? | Open the overlay (outside text fields). |
| Escape | Close it. |
| Contract | States tested |
|---|---|
| Behaviour | closed, open, filtered, empty-filter, gated-when, suppressed |
| Accessibility | keyboard, labeled, semantic-html |
| Visual | default, empty, dark |