Keycap Shortcut
A React component for keycap-styled shortcut labels — combine multiple keys, mod shows ⌘ or Ctrl per platform, and with listening enabled each keycap presses down and springs back when the user actually hits the matching key, with a ring flash once the whole combo is held.
Draws shortcut hints as physical keycaps. Pass a keys array to combine several keys (for example ['mod', 'K']); mod shows ⌘ on mac and Ctrl everywhere else. Turn on listen and, when the user actually presses the matching key, that keycap sinks and springs back on release, and the outer frame flashes a ring once the whole combo is held. The component only observes key presses and never intercepts them, so the page's own shortcuts keep working.
Bring this into your project
Run this command in your project folder. The CLI adds the source and dependencies; then follow the usage example below.
npx webberui@latest add keycap-shortcut.enPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<KeycapShortcut/>
The complete example includes the preview layout and data. Install the component first, then paste this file into your project.
Installation
npx webberui@latest add https://webberui.com/r/keycap-shortcut.en.jsonOr, once registries are configured in components.json, install it as @webberui/keycap-shortcut.
Usage
import { KeycapShortcut } from "@/components/ui/keycap-shortcut";
// Combo: shows ⌘ K on mac, Ctrl K elsewhere
<KeycapShortcut keys={["mod", "K"]} listen />
// Single key, inline in text
<p>
Press <KeycapShortcut>Esc</KeycapShortcut> to close
</p>
// With a separator and the larger size
<KeycapShortcut keys={["shift", "mod", "P"]} separator="+" size="md" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Single key text (e.g. "Esc", "K"); use either this or keys, keys wins |
keys | string[] | — | Key combo array, e.g. ['mod', 'K']; mod shows ⌘ or Ctrl depending on the platform |
listen | boolean | false | Listen for real key presses; when on, pressing the matching key sinks the keycap and springs it back |
size | "sm" | "md" | "sm" | Keycap size (24px / 28px tall) |
separator | React.ReactNode | — | Separator between combo keys (e.g. "+"); none by default |
platform | "auto" | "mac" | "other" | "auto" | Display platform; auto detects after mount, and the first SSR render always uses other |
pressDuration | number | 80 | Press-down duration (ms), also written to --wb-keycap-shortcut-duration |
className | string | — | className merged into the outermost container |
resolveKeycap(token, isMac) is also exported by name: it returns a ResolvedKeycap (display text, screen-reader name, aria-keyshortcuts key name, and match identifiers), so you can reuse the same key-name mapping in your own UI.
ResolvedKeycap
| Field | Type | Default | Description |
|---|---|---|---|
label | string | — | Text or symbol shown on the keycap |
srLabel | string | — | Full name read by screen readers for symbol keycaps; undefined for plain-text keycaps |
aria | string | — | Key name used in aria-keyshortcuts (e.g. Meta, Control, ArrowUp) |
match | string[] | — | Identifiers matched while listening |
How it works
- Key-name mapping:
mod→ ⌘ / Ctrl,alt→ ⌥ / Alt,shift→ ⇧,enter→ ↵,esc→ Esc,tab→ Tab,space→ Space (the Chinese build shows 空白鍵, "space bar"),arrowupand friends → ↑ ↓ ← →,backspace→ ⌫; any other key name is uppercased as-is. - Listeners are attached to window keydown / keyup only while
listenistrue. Matching usese.key(case-insensitive), withe.codefilling in letters and digits — holding ⌥ on mac turnse.keyinto a special symbol, and the right keycap still lights up. Each keycap decides on its own whether its key is held. - No
preventDefault: the component observes and never intercepts, so browser and page shortcuts still fire. - On mac, other keys fire no keyup while ⌘ is held, so releasing ⌘ resets every pressed state; losing window focus (blur) resets them too.
- keydown events during IME composition (
isComposingorkeyCode229) are skipped. - Timing is exposed as CSS variables:
--wb-keycap-shortcut-duration(written frompressDuration) and--wb-keycap-shortcut-ease(defaultcubic-bezier(0.23,1,0.32,1)), both overridable globally. Release uses a spring with stiffness 600 and damping 20.
Accessibility
- The outer element carries
aria-keyshortcuts(e.g.Meta+KorControl+K, per platform), so assistive tech can read the full shortcut - Each keycap is a semantic
kbdelement; symbol keycaps (⌘, ⇧, ↵, arrows, and so on) hide the symbol from assistive tech and read an sr-only full name instead (e.g. "Command", "Shift") - Listening only changes the visuals and never touches any aria attribute, so screen readers are not disturbed by the key animation
- When the user's system has reduce motion turned on, pressing only changes the background color, with no movement or bounce
Avatar
An avatar that shows initials until the image loads and then crossfades, with a breathing presence dot, a hover ring that expands, five sizes and two shapes.
Issue Cascade Board
A dark, ambient-glow issue list where cards cascade in, status pills re-layout on change and edges shimmer on hover — built for product launch sections.