# Kbd Shortcuts

> Keycap and key-combo components with a Mac/Windows symbol map, shown as a shortcut cheatsheet.

- Author: Atelier UI
- License: MIT
- Page: https://chromotion.dev/en/components/kbd
- Site: Atelier UI (https://chromotion.dev)

## Install

```bash
npx shadcn@latest add https://chromotion.dev/r/kbd.json
```

Stack: React + TypeScript + Tailwind CSS v4, shadcn/ui conventions. `cn` comes from `@/lib/utils`.

## Dependencies

No extra dependencies.

## Usage

```tsx title="components/kbd-demo.tsx"
"use client"

import * as React from "react"

import { KbdGroup, type KbdPlatform } from "@/components/kbd"
import { cn } from "@/lib/utils"

const shortcuts: { group: string; items: { label: string; keys: string[] }[] }[] = [
  {
    group: "General",
    items: [
      { label: "Open command palette", keys: ["mod", "K"] },
      { label: "Search files", keys: ["mod", "P"] },
      { label: "Toggle sidebar", keys: ["mod", "B"] },
    ],
  },
  {
    group: "Editing",
    items: [
      { label: "Undo", keys: ["mod", "Z"] },
      { label: "Redo", keys: ["mod", "shift", "Z"] },
      { label: "Duplicate line", keys: ["alt", "shift", "down"] },
      { label: "Delete line", keys: ["mod", "shift", "K"] },
    ],
  },
  {
    group: "Navigation",
    items: [
      { label: "Go to line", keys: ["ctrl", "G"] },
      { label: "Close tab", keys: ["mod", "W"] },
      { label: "Dismiss", keys: ["esc"] },
    ],
  },
]

const platforms: { id: KbdPlatform; label: string }[] = [
  { id: "mac", label: "macOS" },
  { id: "windows", label: "Windows" },
]

export default function KbdDemo() {
  const [platform, setPlatform] = React.useState<KbdPlatform>("mac")

  return (
    <div className="w-full max-w-md rounded-2xl border bg-card p-5 shadow-sm">
      <div className="mb-4 flex items-center justify-between gap-4">
        <h3 className="text-sm font-semibold">Keyboard shortcuts</h3>
        <div role="group" aria-label="Platform" className="inline-flex rounded-lg bg-muted p-0.5">
          {platforms.map((p) => (
            <button
              key={p.id}
              type="button"
              aria-pressed={platform === p.id}
              onClick={() => setPlatform(p.id)}
              className={cn(
                "h-7 rounded-md px-2.5 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring/40",
                platform === p.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
              )}
            >
              {p.label}
            </button>
          ))}
        </div>
      </div>
      <div className="grid gap-5">
        {shortcuts.map((section) => (
          <section key={section.group}>
            <h4 className="mb-1 text-xs font-medium tracking-wide text-muted-foreground uppercase">{section.group}</h4>
            <ul className="divide-y">
              {section.items.map((item) => (
                <li key={item.label} className="flex items-center justify-between gap-4 py-2 text-sm">
                  <span>{item.label}</span>
                  <KbdGroup keys={item.keys} platform={platform} />
                </li>
              ))}
            </ul>
          </section>
        ))}
      </div>
    </div>
  )
}
```

## Source

```tsx title="components/kbd.tsx"
import * as React from "react"

import { cn } from "@/lib/utils"

export type KbdPlatform = "mac" | "windows"

const symbols: Record<string, { mac: string; windows: string }> = {
  mod: { mac: "⌘", windows: "Ctrl" },
  cmd: { mac: "⌘", windows: "Win" },
  ctrl: { mac: "⌃", windows: "Ctrl" },
  alt: { mac: "⌥", windows: "Alt" },
  option: { mac: "⌥", windows: "Alt" },
  shift: { mac: "⇧", windows: "Shift" },
  enter: { mac: "↵", windows: "Enter" },
  return: { mac: "↵", windows: "Enter" },
  backspace: { mac: "⌫", windows: "Backspace" },
  delete: { mac: "⌦", windows: "Del" },
  esc: { mac: "⎋", windows: "Esc" },
  tab: { mac: "⇥", windows: "Tab" },
  space: { mac: "␣", windows: "Space" },
  up: { mac: "↑", windows: "↑" },
  down: { mac: "↓", windows: "↓" },
  left: { mac: "←", windows: "←" },
  right: { mac: "→", windows: "→" },
}

/** Resolves a key token ("mod", "shift", "K") to the glyph for a platform. */
export function resolveKey(key: string, platform: KbdPlatform = "mac") {
  const entry = symbols[key.toLowerCase()]
  if (entry) return entry[platform]
  return key.length === 1 ? key.toUpperCase() : key
}

export interface KbdProps extends React.ComponentProps<"kbd"> {
  /** A key token such as "mod", "shift", "enter" or a literal like "K". Falls back to children. */
  keyName?: string
  platform?: KbdPlatform
}

export function Kbd({ keyName, platform = "mac", className, children, ...props }: KbdProps) {
  return (
    <kbd
      className={cn(
        "inline-flex h-6 min-w-6 items-center justify-center rounded-md border border-b-2 bg-muted px-1.5 font-sans text-xs font-medium text-muted-foreground shadow-xs",
        className
      )}
      {...props}
    >
      {keyName ? resolveKey(keyName, platform) : children}
    </kbd>
  )
}

export interface KbdGroupProps extends Omit<React.ComponentProps<"span">, "children"> {
  /** Combo as key tokens, e.g. ["mod", "K"] */
  keys: string[]
  platform?: KbdPlatform
  /** Show "+" between keys (the Mac style is usually joined without it) */
  separator?: boolean
}

export function KbdGroup({ keys, platform = "mac", separator, className, ...props }: KbdGroupProps) {
  const showSeparator = separator ?? platform === "windows"
  const label = keys.map((k) => resolveKey(k, platform)).join(" ")

  return (
    <span
      role="group"
      aria-label={label}
      className={cn("inline-flex items-center gap-1", className)}
      {...props}
    >
      {keys.map((key, i) => (
        <React.Fragment key={`${key}-${i}`}>
          {i > 0 && showSeparator && (
            <span aria-hidden className="text-xs text-muted-foreground/70">
              +
            </span>
          )}
          <Kbd keyName={key} platform={platform} aria-hidden />
        </React.Fragment>
      ))}
    </span>
  )
}
```
