# Kbd (/docs/display/kbd)



<ComponentShowcase name="kbd" />

## Installation [#installation]

```bash
npx shadcn@latest add https://ui.zyeon.ai/r/kbd.json
```

## Prompt [#prompt]

The prompt behind this component — paste it into your AI assistant to recreate or adapt it.

```text
Build a React + TypeScript + Tailwind "Kbd" / "KbdCombo" pair for displaying
keyboard shortcuts.

Contract
- Kbd: forwardRef<HTMLElement, KbdProps> rendering a real <kbd> element.
  extends React.HTMLAttributes<HTMLElement>. Props: size?: "sm" | "md"
  (default "md"); platform?: "mac" | "win" (forces modifier glyphs; omit to
  auto-detect from the browser). className merged via cn(), rest props spread
  on the <kbd>.
- KbdCombo: forwardRef<HTMLSpanElement, KbdComboProps> rendering a <span> of
  Kbd keycaps. Props: keys: string[] (semantic key names in press order, e.g.
  ["mod", "K"]); separator?: ReactNode (default "+"), rendered between
  keycaps; size and platform forwarded to every Kbd.

Behavior
- Both accept semantic key names, not just literal glyphs. When a Kbd's
  string child matches a known name it resolves to a glyph; anything else
  (a plain letter like "K", or an already-resolved symbol like "⌘") renders
  unchanged. Matching is case-insensitive.
- Two lookup tables: a platform-aware one (mod → ⌘ on mac / Ctrl on win; cmd →
  ⌘ / Win; ctrl → ⌃ / Ctrl; alt/option → ⌥ / Alt) and a platform-independent
  one (shift → ⇧, enter/return → ↵, esc/escape → Esc, tab → ⇥, backspace → ⌫,
  delete → ⌦, capslock → ⇪, space → Space, arrow keys → ↑↓←→).
- Platform detection is SSR-safe: read via useSyncExternalStore with a
  getServerSnapshot that always returns "win", and a client snapshot that
  checks navigator.platform / navigator.userAgent for "mac". This renders
  "win" glyphs on the server and on the client's first paint, then React
  re-renders once with the real client value — no hydration mismatch, no
  useEffect + setState. An explicit `platform` prop always wins over
  detection.
- KbdCombo maps each string in `keys` through a Kbd, interleaving the
  separator (aria-hidden, since it's punctuation, not a key) between them.

Rendering & styling
- Semantic tokens only: bg-muted for the keycap surface, text-muted-foreground
  for the glyph, border (the theme's default border color) for the outline.
  No hardcoded colors — the keycap follows the host theme and dark mode.
- Keycap: rounded-md border with a doubled bottom border (border border-b-2)
  to read as a physical cap with depth, inline-flex centered content,
  align-middle so it sits correctly inside a text line, select-none since a
  keycap glyph isn't meant to be selected/copied as text.
- Two sizes: sm (h-5, text-[10px]) for inline-in-text use, md (h-6, text-xs)
  as the default for standalone hints. No animation.

Customization levers
- Sizing: sm/md cover inline vs standalone; add a documented lg entry to
  SIZE_CLASSES if a settings page needs a bigger keycap — don't invent a
  one-off className override at every call site.
- Light/dark contrast: bg-muted / text-muted-foreground / border already
  swap with the theme; only touch this if a surface needs a stronger keycap
  (e.g. bg-secondary) for contrast against a busy background.
- Mapping table: PLATFORM_SYMBOLS and KEY_SYMBOLS are the single source of
  truth — add an entry for a key name your product uses often (e.g. a
  "windows" logo glyph) instead of hardcoding the glyph at each call site.
- Tooltip pairing: Kbd/KbdCombo are display-only; wrap one in a shadcn
  Tooltip trigger to show "Command Palette" text alongside the shortcut, or
  place it as the Tooltip's content next to an icon button.
```

## Concepts [#concepts]

<Mermaid
  chart="`flowchart TD
A[&#x22;key name (e.g. mod)&#x22;] --> B{&#x22;platform-aware?&#x22;}
B -->|&#x22;mod / cmd / ctrl / alt&#x22;| C[&#x22;useSyncExternalStore<br/>(server + first paint: win)&#x22;]
B -->|&#x22;shift / enter / esc / etc&#x22;| D[&#x22;static symbol table&#x22;]
C --> E[&#x22;glyph for detected/forced platform&#x22;]
D --> E
E --> F[&#x22;rendered inside a real kbd element&#x22;]
G[&#x22;KbdCombo keys[]&#x22;] --> H[&#x22;each key resolved via Kbd&#x22;]
H --> I[&#x22;joined by separator (default +)&#x22;]`"
/>

* **Semantic kbd markup** — every keycap is a real `<kbd>` element, not a styled `<span>`, so assistive tech and browser find-in-page treat it as keyboard input, not decoration.
* **Platform-aware modifier glyphs** — `mod` is the portable "primary shortcut modifier": it reads as ⌘ on mac and Ctrl on windows/linux, so one prompt/one call site works everywhere instead of branching per OS at every usage.
* **SSR-safe client detection** — `useSyncExternalStore` (not `useEffect` + `setState`) reads `navigator.platform` only on the client while giving the server a fixed snapshot, so the server-rendered HTML and the client's first paint always agree — no flash-of-wrong-glyph, no hydration warning.
* **Key-name-to-glyph mapping** — a plain lookup table turns semantic names ("mod", "shift", "esc") into their glyphs; unrecognized strings pass through untouched, so `<Kbd>K</Kbd>` just renders "K".
* **Combo as separated sequence** — `KbdCombo` is not a new visual primitive, it's `Kbd` repeated with an `aria-hidden` separator between each — the combination reads correctly to screen readers as a sequence of keys, not as one run-on word.
