# Preset keys

Tabs and segmented controls as interlocked preset keys, like a car radio's: press one and it latches down while the one that was down springs back up. Only one is ever down. PresetTabs switches panels; PresetKeys picks a value.

- Category: Essentials
- Docs: https://bevel-ui.vercel.app/components/preset-keys
- Import from: `@/components/ui/preset-keys`

## Install

```bash
npx shadcn@latest add @bevel/preset-keys
```

npm dependencies (installed by the CLI): `@base-ui/react`, `cn`.

## Anatomy

```tsx
<PresetTabs defaultValue="preview">
  <PresetTabsList size="sm">
    <PresetTabsTab value="preview" />
    <PresetTabsTab value="code" />
  </PresetTabsList>
  <PresetTabsPanel value="preview" />
  <PresetTabsPanel value="code" />
</PresetTabs>

<PresetKeys aria-label="Range" value={range} onValueChange={setRange}>
  <PresetKey value="7d" />
  <PresetKey value="30d" />
</PresetKeys>
```

## Parts

| Part | Renders | Description |
| --- | --- | --- |
| `PresetTabs` | Tabs.Root | value, defaultValue, onValueChange, orientation. Arrow keys move along the keys; Enter or Space presses one. |
| `PresetTabsList` | Tabs.List | The keybed. size sm for a Preview/Code row or a toolbar, md for a screen's sections. activateOnFocus to press as you arrow. |
| `PresetTabsTab` | Tabs.Tab | One key, with a lamp lit while it's down. value, disabled. |
| `PresetTabsPanel` | Tabs.Panel | The panel a key shows. value, keepMounted. |
| `PresetKeys` | RadioGroup | A segmented control: value, defaultValue, onValueChange, name, disabled, size. Exactly one key is down; arrow keys move and press. Give it an aria-label. |
| `PresetKey` | Radio | One key. value, disabled. |

## Example: Preview and code

The tabs over a docs example, at the small size.

```tsx
"use client"

import { PresetTabs, PresetTabsList, PresetTabsPanel, PresetTabsTab } from "@/components/ui/preset-keys"

const code = `<PresetTabs defaultValue="preview">
  <PresetTabsList size="sm" aria-label="Example">
    <PresetTabsTab value="preview">Preview</PresetTabsTab>
    <PresetTabsTab value="code">Code</PresetTabsTab>
  </PresetTabsList>
  <PresetTabsPanel value="preview">…</PresetTabsPanel>
  <PresetTabsPanel value="code">…</PresetTabsPanel>
</PresetTabs>`

/** Preview and Code for a docs example: the small keys, over the panels they switch. */
export function PresetKeysExample() {
  return (
    <PresetTabs defaultValue="preview" className="w-full">
      <PresetTabsList size="sm" aria-label="Example">
        <PresetTabsTab value="preview">Preview</PresetTabsTab>
        <PresetTabsTab value="code">Code</PresetTabsTab>
      </PresetTabsList>
      <PresetTabsPanel value="preview" className="grid h-40 place-items-center rounded-xl border bg-card text-sm text-muted-foreground">
        The component, live.
      </PresetTabsPanel>
      <PresetTabsPanel value="code" className="h-40 overflow-auto rounded-xl border bg-muted p-3">
        <pre className="font-mono text-xs leading-relaxed">{code}</pre>
      </PresetTabsPanel>
    </PresetTabs>
  )
}
```

## Example: Time range

A segmented range picker over a usage chart.

```tsx
"use client"

import * as React from "react"
import { PresetKey, PresetKeys } from "@/components/ui/preset-keys"

const ranges = {
  "24h": { label: "Last 24 hours", runs: [4, 7, 3, 9, 12, 8, 6, 10, 14, 9, 5, 7] },
  "7d": { label: "Last 7 days", runs: [52, 61, 48, 70, 66, 31, 28] },
  "30d": { label: "Last 30 days", runs: [210, 260, 240, 300, 280, 330, 310, 350, 290, 320] },
  "90d": { label: "Last 90 days", runs: [800, 950, 1100, 1020, 1240, 1180, 1300, 1420, 1390, 1500, 1610, 1580] },
} as const

type Range = keyof typeof ranges

/** A time-range picker over a usage chart: one range is always down. */
export function PresetKeysRange() {
  const [range, setRange] = React.useState<Range>("7d")
  const { label, runs } = ranges[range]
  const max = Math.max(...runs)
  const total = runs.reduce((a, b) => a + b, 0)
  return (
    <div className="flex w-full flex-col gap-4 rounded-xl border bg-card p-5">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div>
          <p className="text-sm font-semibold">Agent runs</p>
          <p className="text-xs text-muted-foreground">
            {label}: {total.toLocaleString("en-US")}
          </p>
        </div>
        <PresetKeys<Range> aria-label="Range" size="sm" value={range} onValueChange={setRange}>
          {(Object.keys(ranges) as Range[]).map((r) => (
            <PresetKey<Range> key={r} value={r}>
              {r}
            </PresetKey>
          ))}
        </PresetKeys>
      </div>
      <div aria-hidden className="flex h-24 items-end gap-1">
        {runs.map((n, i) => (
          <span key={i} className="flex-1 rounded-sm bg-foreground/15" style={{ height: `${(n / max) * 100}%` }} />
        ))}
      </div>
    </div>
  )
}
```
