# Toggle

> A two-state button, pressed or not, for toolbar formatting and view options.

Category: Forms. Docs page: https://rime.mjsons.net/components/toggle

## Installation

```bash
npx shadcn@latest add KezLahd/rime/toggle
```

Toggle is a real <button> with aria-pressed: quiet at rest, washed in the accent with a hairline when pressed. ghost (the default) has no edge at rest, for toolbars; outline keeps a hairline box, for a lone option such as "Show archived".

Use Switch for a setting that applies at once and reads as on or off; use Checkbox inside a form that is submitted.

## Usage

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

<Toggle aria-label="Bold" icon={<Bold size={16} />} pressed={bold} onPressedChange={setBold} />
```

## Props: Toggle

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `pressed` | `boolean` |  | Controlled pressed state. Leave unset (with defaultPressed) for uncontrolled. |
| `defaultPressed` | `boolean` | `false` |  |
| `onPressedChange` | `(pressed: boolean) => void` |  |  |
| `variant` | `"ghost" \| "outline"` | `"ghost"` | ghost (default) shows no edge at rest; outline keeps a hairline box. |
| `size` | `"sm" \| "md"` | `"md"` |  |
| `icon` | `ReactNode` |  | An icon before the label. An icon-only toggle needs aria-label. |

Also accepts the native attributes of: AriaAttributes, ButtonHTMLAttributes, DOMAttributes, HTMLAttributes.

## Examples

### Icon, text and disabled

```tsx
<Toggle aria-label="Bold" icon={<Bold size={16} />} pressed={bold} onPressedChange={setBold} />
<Toggle variant="outline" pressed={archived} onPressedChange={setArchived}>Show archived</Toggle>
<Toggle disabled>Disabled</Toggle>
```

## Theme tokens

Per-component tokens. Undeclared by default: the default below is the fallback the CSS uses. Set any of them at :root or on a wrapper to restyle every instance.

| Token | Default |
| --- | --- |
| `--toggle-height-md` | `var(--control-h-md)` |
| `--toggle-height-sm` | `var(--control-h-sm)` |
| `--toggle-ink` | `var(--ink-muted)` |
| `--toggle-on-bg` | `var(--wash-active)` / `var(--surface-solid)` |
| `--toggle-on-ink` | `var(--ink-brand)` |
| `--toggle-on-line` | `var(--line-brand-strong)` |
| `--toggle-radius` | `var(--r-md)` / `var(--r-sm)` |

System tokens it reads: `--border-w`, `--control-h-md`, `--control-h-sm`, `--density`, `--dur`, `--ease`, `--focus-ring`, `--font-body`, `--ink-brand`, `--ink-disabled`, `--ink-heading`, `--ink-muted`, `--line`, `--line-brand-strong`, `--line-input`, `--r-md`, `--r-sm`, `--recess-fill`, `--rgb-white`, `--shadow-hairline-card`, `--shadow-recess`, `--surface-solid`, `--text-small`, `--wash-active`, `--wash-hover`.

## Accessibility

- aria-pressed carries the state, so the label must not change with it: "Bold", pressed or not pressed.
- An icon-only toggle needs aria-label.

## Do

- Keep the label constant; the pressed state says the rest.

## Don't

- Don't use a Toggle to trigger a one-off action; that is a Button.

Related: https://rime.mjsons.net/components/toggle-group, https://rime.mjsons.net/components/switch
