# Kbd

Displays a keyboard key or a combination of keys.

```tsx
import * as stylex from "@stylexjs/stylex"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { VStack } from "@/components/ui/stack"
import { color, leading, text } from "@/theme/tokens.stylex"

const styles = stylex.create({
  plus: { color: color.mutedForeground, fontSize: text.xs, lineHeight: leading.xs },
})

export default function KbdDemo() {
  return (
    <VStack gap={4} align="center">
      <KbdGroup>
        <Kbd>⌘</Kbd>
        <Kbd>⇧</Kbd>
        <Kbd>⌥</Kbd>
        <Kbd>⌃</Kbd>
      </KbdGroup>
      <KbdGroup>
        <Kbd>Ctrl</Kbd>
        <span {...stylex.props(styles.plus)}>+</span>
        <Kbd>B</Kbd>
      </KbdGroup>
    </VStack>
  )
}
```

## Installation

1. Install the following dependencies:

   ```bash
   npm install @base-ui/react @stylexjs/stylex clsx
   ```

2. Set up the token layer and the StyleX compiler, if you have not already. See
   [Installation](https://carbon.style/docs/installation), and paste `states.ts` from the same page.
   The keycap's surface comes from it. `Kbd` also needs `interop.css`, which sizes
   an icon inside a key.

3. Copy and paste the following code into your project.

   ```tsx title="components/ui/kbd.tsx"
   "use client"

   import { useRender } from "@base-ui/react/use-render"
   import * as stylex from "@stylexjs/stylex"
   import type { StyleXStyles } from "@stylexjs/stylex"
   import { clsx } from "clsx"

   import { color, font, leading, radius, space, text } from "@/theme/tokens.stylex"

   const styles = stylex.create({
     kbd: {
       display: "inline-flex",
       alignItems: "center",
       justifyContent: "center",
       flexShrink: 0,
       gap: space.s0_5,
       height: space.s5,
       minWidth: space.s5,
       paddingInline: space.s1,
       borderRadius: radius.sm,
       borderWidth: 1,
       borderStyle: "solid",
       borderColor: color.border,
       backgroundColor: color.muted,
       color: color.mutedForeground,
       fontFamily: font.mono,
       fontSize: text.xs,
       lineHeight: leading.xs,
       fontWeight: 500,
       whiteSpace: "nowrap",
       userSelect: "none",
       "--carbon-icon-size": space.s3,
     },
     group: {
       display: "inline-flex",
       alignItems: "center",
       gap: space.s1,
     },
   })

   const kbdStyles = {
     base: styles.kbd,
     group: styles.group,
   }

   type Overrides = { xstyle?: StyleXStyles; className?: string }

   type KbdProps = useRender.ComponentProps<"kbd"> & Overrides

   function Kbd({ render, ref, style, xstyle, className, ...props }: KbdProps) {
     const sx = stylex.props(styles.kbd, xstyle)
     return useRender({
       defaultTagName: "kbd",
       render,
       ref,
       props: {
         "data-slot": "kbd",
         ...sx,
         ...props,
         className: clsx("carbon-icons", sx.className, className),
         style: { ...sx.style, ...style },
       },
     })
   }

   function KbdGroup({ render, ref, style, xstyle, className, ...props }: KbdProps) {
     const sx = stylex.props(styles.group, xstyle)
     return useRender({
       defaultTagName: "kbd",
       render,
       ref,
       props: {
         "data-slot": "kbd-group",
         ...sx,
         ...props,
         className: clsx(sx.className, className),
         style: { ...sx.style, ...style },
       },
     })
   }

   export { Kbd, KbdGroup, kbdStyles }
   ```

4. Update the import paths to match your project setup.

## Usage

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

```tsx
<KbdGroup>
  <Kbd>⌘</Kbd>
  <Kbd>K</Kbd>
</KbdGroup>
```

## Group

Wrap keys pressed together in `KbdGroup`. It renders a `kbd` of `kbd`s, which
is how HTML marks up a combination, and sits inline in running text.

```tsx
import * as stylex from "@stylexjs/stylex"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { color, leading, text } from "@/theme/tokens.stylex"

const styles = stylex.create({
  text: { color: color.mutedForeground, fontSize: text.sm, lineHeight: leading.sm },
})

export default function KbdGroupDemo() {
  return (
    <p {...stylex.props(styles.text)}>
      Use{" "}
      <KbdGroup>
        <Kbd>⌘</Kbd>
        <Kbd>K</Kbd>
      </KbdGroup>{" "}
      to open the command palette, or <Kbd>Esc</Kbd> to close it.
    </p>
  )
}
```

## Input group

Put a shortcut at the end of a search field with an `InputGroupAddon`.

```tsx
import * as stylex from "@stylexjs/stylex"
import { Input } from "@/components/ui/input"
import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
import { Kbd, KbdGroup } from "@/components/ui/kbd"
import { Search } from "lucide-react"

const styles = stylex.create({
  group: { width: "100%", maxWidth: "20rem" },
})

export default function KbdInputGroup() {
  return (
    <InputGroup xstyle={styles.group}>
      <Input placeholder="Search..." aria-label="Search" />
      <InputGroupAddon>
        <Search />
      </InputGroupAddon>
      <InputGroupAddon align="inline-end">
        <KbdGroup>
          <Kbd>⌘</Kbd>
          <Kbd>K</Kbd>
        </KbdGroup>
      </InputGroupAddon>
    </InputGroup>
  )
}
```

## API reference

Pass `render` to replace a part's element or compose it with another component.

Both parts render a `kbd` through Base UI's
[`useRender`](https://base-ui.com/react/utils/use-render), and take `xstyle`,
`className` and inline `style` alongside every `kbd` attribute.

### Kbd

| Prop        | Type                                               | Default   |
| ----------- | -------------------------------------------------- | --------- |
| `render`    | `ReactElement \| ((props, state) => ReactElement)` | `<kbd />` |
| `xstyle`    | `StyleXStyles`                                     | —         |
| `className` | `string`                                           | —         |

A flat keycap, `1.25rem` tall and at least as wide, in the monospace face. It
is not focusable.

### KbdGroup

An inline row of keys with a `0.25rem` gap. Text between keys, such as a `+`,
sits in the same row.

### kbdStyles

The style maps behind the component. Spread them onto any element.

| Key     | What it is                             |
| ------- | -------------------------------------- |
| `base`  | The cap: fill, hairline, size and type |
| `group` | The inline row                         |

### Data attributes

| Attribute               | On        | Effect                          |
| ----------------------- | --------- | ------------------------------- |
| `data-slot="kbd"`       | a key     | Targets a key from a parent     |
| `data-slot="kbd-group"` | the group | Targets the group from a parent |

### CSS variables

| Variable             | On    | Effect                                                   |
| -------------------- | ----- | -------------------------------------------------------- |
| `--carbon-icon-size` | a key | The icon's size, read by `interop.css`. Defaults to 12px |
