# Keyboard shortcut

Keyboard shortcuts drawn as box-drawing keycaps, with Mac and PC labels and caps that press down while the visitor holds the keys.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown variants are available by appending `.md` to any URL or sending an `Accept: text/markdown` header. The asciicn agent skill, for building any ASCII effect on the engine, is at [/.well-known/agent-skills/asciicn/SKILL.md](/.well-known/agent-skills/asciicn/SKILL.md).

> **This is an example, not the only option.** It is one component built on the asciicn engine. Install it when it is what you were asked for. For any other look, don't stretch its props to fit: write your own field on `AsciiShader` ([Write your own](/docs/engine/write-your-own.md)), or draw a CPU grid with `AsciiGrid`, and read this example's source for technique. Its full source is in [https://asciicn.dev/r/kbd-shortcut.json](https://asciicn.dev/r/kbd-shortcut.json), under `files[].content`.

## Installation [#installation]

```bash
npx shadcn@latest add https://asciicn.dev/r/kbd-shortcut.json
```

## Usage [#usage]

```tsx
import { KbdShortcut } from "@/components/asciicn/kbd-shortcut";

export function PaletteHint() {
  return <KbdShortcut keys="mod+shift+p" />;
}
```

`keys` is the shortcut as text. Join the keys of a chord with `+`, and separate steps with a space: `"g i"` draws `G then I`. Write `++` or a lone `+` for the plus key.

`mod` is ⌘ on a Mac and Ctrl elsewhere. Other names: `ctrl`, `alt` (or `option`), `shift`, `meta` (or `cmd`), `enter`, `esc`, `tab`, `space`, `backspace`, `delete` and the arrows `up`, `down`, `left` and `right`. Anything else is shown as written, with single letters in capitals.

On a Mac the caps show symbols, `⌘ ⇧ P`. On a PC they show words joined by `+`, `Ctrl + Shift + P`. `platform="auto"` picks from the visitor's browser after the page loads; set `"mac"` or `"pc"` to fix it.

A `cap` keycap is three rows of box-drawing glyphs with a heavy bottom edge for depth:

```text
┌─────┐ ┌───┐ ┌───┐
│  ⌘  │ │ ⇧ │ │ P │
┕━━━━━┙ ┕━━━┙ ┕━━━┙
```

With `listen`, a cap presses down while the visitor holds its key: the bottom edge goes flat and the label turns to reverse video. `onTrigger` runs each time the whole chord goes down.

`variant="inline"` sets each key in brackets on one row, `[⌘] [K]`, to sit inside menus and running text.

## Examples [#examples]

### Menu [#menu]

Inline shortcuts aligned to the right of each action.

```tsx title="examples/kbd-shortcut-menu.tsx"
import { KbdShortcut } from "@/components/asciicn/kbd-shortcut";

const ACTIONS = [
  { keys: "mod+k", label: "Search docs" },
  { keys: "mod+shift+c", label: "Copy as ANSI" },
  { keys: "mod+e", label: "Export PNG" },
  { keys: "g s", label: "Go to Studio" },
  { keys: "?", label: "All shortcuts" },
] as const;

export default function KbdShortcutMenu() {
  return (
    <ul className="bg-background border-border flex w-full max-w-sm flex-col rounded-lg border p-1.5 text-sm">
      {ACTIONS.map((action) => (
        <li
          key={action.label}
          className="hover:bg-muted flex items-center justify-between gap-4 rounded-md px-3 py-2"
        >
          <span className="truncate">{action.label}</span>
          <KbdShortcut keys={action.keys} variant="inline" />
        </li>
      ))}
    </ul>
  );
}
```

### Cheat sheet [#cheat-sheet]

Keycaps in grey, in two groups.

```tsx title="examples/kbd-shortcut-cheatsheet.tsx"
import { KbdShortcut } from "@/components/asciicn/kbd-shortcut";

const GROUPS = [
  {
    items: [
      { keys: "space", label: "Play or pause" },
      { keys: "left", label: "Step back a frame" },
      { keys: "right", label: "Step forward a frame" },
    ],
    title: "Playback",
  },
  {
    items: [
      { keys: "mod+z", label: "Undo" },
      { keys: "mod+shift+z", label: "Redo" },
      { keys: "mod+s", label: "Save preset" },
    ],
    title: "Editing",
  },
] as const;

export default function KbdShortcutCheatsheet() {
  return (
    <div className="grid w-full max-w-2xl gap-8 sm:grid-cols-2">
      {GROUPS.map((group) => (
        <section key={group.title} className="flex flex-col gap-3">
          <h3 className="text-muted-foreground font-mono text-xs">
            {group.title}
          </h3>
          <ul className="flex flex-col gap-3">
            {group.items.map((item) => (
              <li
                key={item.label}
                className="flex items-center justify-between gap-4 text-sm"
              >
                <span>{item.label}</span>
                <KbdShortcut keys={item.keys} color="grey" />
              </li>
            ))}
          </ul>
        </section>
      ))}
    </div>
  );
}
```

## Props [#props]

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `keys` (required) | `string` |  | The shortcut: + joins a chord, a space separates steps, as in "mod+shift+p" or "g i". |
| `variant` | `"cap" \| "inline"` | `"cap"` | Three-row keycaps, or keys in brackets on one row. |
| `platform` | `"auto" \| "mac" \| "pc"` | `"auto"` | Mac symbols or PC words. auto reads the visitor's platform. |
| `listen` | `boolean` | `false` | Press the caps down while the visitor holds those keys. |
| `onTrigger` | `() => void` |  | With listen, called once each time the whole chord goes down. One-step shortcuts only. |
| `color` | `AsciiColor` | `"ink"` | Label colour, a palette name or any CSS colour. The frame uses it at 55%. |
| `size` | `"sm" \| "md" \| "lg"` | `"md"` | Glyph cell height: 12, 16 or 20px. |
| `cell` | `number` |  | Glyph cell height in CSS pixels. Overrides size. |

## Notes [#notes]

* The root and each key are `<kbd>` elements, as HTML nests them for a key combination. Box-drawing glyphs are hidden from screen readers, and each symbol has a spoken name, so `⌘` reads as "Command".
* `listen` only watches the keyboard; it never stops the browser or the page from handling a shortcut. Pair it with your own handler.
* The server renders PC labels. On a Mac, `platform="auto"` swaps in the symbols right after hydration.
* Pressing is instant, with no animation, because it answers the visitor's own keys.