# ToggleGroup

> A set of toggles sharing one value: single (a clearable radio set) or multiple.

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

## Installation

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

ToggleGroup sits its items on one recessed track and lifts the pressed ones out as solid keys. type="single" allows at most one pressed item, and pressing it again clears the value; type="multiple" allows any number.

It has one tab stop: arrow keys move between items and Space or Enter presses. For a view switch where one option is always chosen, SegmentedControl reads better.

## Usage

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

<ToggleGroup
  type="single"
  aria-label="Text alignment"
  value={align}
  onValueChange={setAlign}
  items={[
    { value: "left", icon: <AlignLeft size={16} />, "aria-label": "Align left" },
    { value: "center", icon: <AlignCenter size={16} />, "aria-label": "Align centre" },
  ]}
/>
```

## Props: ToggleGroup

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items` (required) | `ReadonlyArray<ToggleGroupItem>` |  |  |
| `aria-label` (required) | `string` |  | Names the group: "Text formatting", "Days". |
| `variant` | `"ghost" \| "outline" \| "ghost" \| "outline"` | `"outline"` |  |
| `size` | `"sm" \| "md" \| "sm" \| "md"` | `"md"` |  |
| `orientation` | `"horizontal" \| "vertical" \| "horizontal" \| "vertical"` | `"horizontal"` |  |
| `disabled` | `boolean` |  |  |
| `className` | `string` |  |  |
| `type` (required) | `"single" \| "multiple"` |  | single: at most one item pressed, like a radio group that can be cleared. |
| `value` (required) | `string \| null \| ReadonlyArray<string>` |  |  |
| `onValueChange` (required) | `(value: string \| null) => void \| (value: string[]) => void` |  |  |

## Examples

### Single and multiple

```tsx
<ToggleGroup type="single" aria-label="Text alignment" value={align} onValueChange={setAlign} items={ALIGN} />
<ToggleGroup type="multiple" aria-label="Text formatting" value={marks} onValueChange={setMarks} items={MARKS} />
```

### Text items

```tsx
<ToggleGroup type="multiple" size="sm" aria-label="Days" value={days} onValueChange={setDays}
  items={["Mon", "Tue", "Wed"].map((d) => ({ value: d.toLowerCase(), label: d }))} />
```

## 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

- single renders role="radiogroup" with role="radio" items and aria-checked; multiple renders role="group" with aria-pressed buttons.
- Roving tabindex: one tab stop, arrows move (Up and Down when vertical), Home and End jump to the ends.
- aria-label on the group is required; icon-only items each need their own aria-label.

## Do

- Use multiple for independent options (bold, italic).
- Keep items to one short word or a familiar icon.

## Don't

- Don't use single where a value is always required; use SegmentedControl.

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