# Kbd

Shows a keyboard key or shortcut.

Source: [kbd.tsx](https://github.com/jakejarvis/jarvis-ui/blob/main/src/components/ui/kbd.tsx)

```tsx
import { Kbd } from "@/components/ui/kbd";

export default function KbdKeys() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Kbd>⌘</Kbd>
      <Kbd>⇧</Kbd>
      <Kbd>⌥</Kbd>
      <Kbd>Enter</Kbd>
      <Kbd>Esc</Kbd>
    </div>
  );
}
```

## Installation

Add the registry, once per project:

```bash
npx shadcn@latest registry add @jarvis=https://ui.jarv.is/r/{name}.json
```

Then add the component:

```bash
npx shadcn@latest add @jarvis/kbd
```

## Usage

```tsx
import { Kbd, KbdGroup } from "@/components/ui/kbd";

export function SearchShortcut() {
  return (
    <KbdGroup>
      <Kbd>⌘</Kbd>
      <Kbd>K</Kbd>
    </KbdGroup>
  );
}
```

Screen readers don't read symbols like ⌘ reliably, so also set `aria-keyshortcuts` on the control
the shortcut triggers.

## Composition

- `KbdGroup`
  - `Kbd`

## Examples

### Shortcuts

`KbdGroup` holds the keys of one shortcut.

```tsx
import { Kbd, KbdGroup } from "@/components/ui/kbd";

export default function KbdShortcuts() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <KbdGroup>
        <Kbd>⌘</Kbd>
        <Kbd>K</Kbd>
      </KbdGroup>
      <KbdGroup>
        <Kbd>Ctrl</Kbd>
        <span className="text-faint">+</span>
        <Kbd>B</Kbd>
      </KbdGroup>
    </div>
  );
}
```

### On a raised surface

```tsx
import { Kbd, KbdGroup } from "@/components/ui/kbd";

export default function KbdRaised() {
  return (
    <div className="flex flex-wrap items-center gap-3 rounded-md bg-card p-3 shadow-raised">
      <Kbd>⌘</Kbd>
      <Kbd>⇧</Kbd>
      <Kbd>Enter</Kbd>
      <Kbd>Esc</Kbd>
      <KbdGroup>
        <Kbd>⌘</Kbd>
        <Kbd>K</Kbd>
      </KbdGroup>
      <KbdGroup>
        <Kbd>Ctrl</Kbd>
        <span className="text-faint">+</span>
        <Kbd>B</Kbd>
      </KbdGroup>
    </div>
  );
}
```

### Small

`size="sm"` fits hints and tooltips.

```tsx
import { Kbd, KbdGroup } from "@/components/ui/kbd";

export default function KbdSmall() {
  return (
    <div className="flex flex-wrap items-center gap-3">
      <Kbd size="sm">⌘</Kbd>
      <Kbd size="sm">⇧</Kbd>
      <Kbd size="sm">⌥</Kbd>
      <Kbd size="sm">Enter</Kbd>
      <Kbd size="sm">Esc</Kbd>
      <KbdGroup>
        <Kbd size="sm">⌘</Kbd>
        <Kbd size="sm">K</Kbd>
      </KbdGroup>
      <span className="flex items-center gap-2 text-2xs font-medium text-muted-foreground">
        <Kbd size="sm">B</Kbd>
        Toggle
      </span>
    </div>
  );
}
```

### In text

```tsx
import { Kbd, KbdGroup } from "@/components/ui/kbd";

export default function KbdInText() {
  return (
    <p className="text-sm text-muted-foreground">
      Press{" "}
      <KbdGroup>
        <Kbd>⌘</Kbd>
        <Kbd>K</Kbd>
      </KbdGroup>{" "}
      to open the command menu.
    </p>
  );
}
```

### In a tooltip

Show an icon button's shortcut in its tooltip.

```tsx
import { SaveIcon } from "lucide-react";

import { Button } from "@/components/ui/button";
import { Kbd } from "@/components/ui/kbd";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

export default function KbdTooltip() {
  return (
    <Tooltip>
      <TooltipTrigger
        render={
          <Button variant="secondary" size="icon" aria-label="Save" aria-keyshortcuts="Meta+S" />
        }
      >
        <SaveIcon />
      </TooltipTrigger>
      <TooltipContent>
        Save
        <Kbd size="sm">⌘S</Kbd>
      </TooltipContent>
    </Tooltip>
  );
}
```

### Platform shortcuts

`useShortcutDisplay`, from the `use-shortcut` hook, shows a chord the way the reader's platform writes it, such as `⌘` `K` on a
Mac and `Ctrl` `K` elsewhere. It takes a [TanStack Hotkeys](https://tanstack.com/hotkeys) chord,
where `Mod` is `⌘` on a Mac and `Ctrl` elsewhere, and returns `keys`, one label per key for a
`KbdGroup`, and `label`, the keys joined for a single `Kbd`: `⌘K` on a Mac and `Ctrl+K` elsewhere.
The same file exports `ShortcutChord`, the type of a chord, and `useShortcut`, which runs a
callback when a chord is pressed: TanStack Hotkeys'
[`useHotkey`](https://tanstack.com/hotkeys/latest/docs/framework/react/reference/functions/useHotkey),
renamed.

```tsx
import { Kbd, KbdGroup } from "@/components/ui/kbd";
import { useShortcutDisplay, type ShortcutChord } from "@/hooks/use-shortcut";

const shortcuts: { label: string; shortcut: ShortcutChord }[] = [
  { label: "Search", shortcut: "Mod+K" },
  { label: "Command palette", shortcut: "Mod+Shift+P" },
  { label: "Move line up", shortcut: "Alt+ArrowUp" },
];

function Shortcut({ label, shortcut }: (typeof shortcuts)[number]) {
  const display = useShortcutDisplay(shortcut);

  return (
    <li className="flex h-5 items-center justify-between gap-6">
      {label}
      {display && (
        <KbdGroup>
          {display.keys.map((key) => (
            <Kbd key={key}>{key}</Kbd>
          ))}
        </KbdGroup>
      )}
    </li>
  );
}

export default function KbdPlatform() {
  return (
    <ul className="flex w-60 flex-col gap-2 text-sm">
      {shortcuts.map((item) => (
        <Shortcut key={item.label} {...item} />
      ))}
    </ul>
  );
}
```

The server can't tell the reader's platform, so the hook returns `null` until the page hydrates,
and the example shows no keys until then rather than guessing and correcting itself. The
[Command](https://ui.jarv.is/components/command.md) menu uses both hooks to show and bind its items' shortcuts.

To add the hook on its own:

```bash
npx shadcn@latest add @jarvis/use-shortcut
```

## Props

| Part | Extends |
| --- | --- |
| `Kbd` | `<kbd>` |
| `KbdGroup` | `<kbd>` |

### Kbd

| Prop    | Type                | Description          |
| ------- | ------------------- | -------------------- |
| `size?` | `"default" \| "sm"` | Default: `"default"` |
