# Toggle

A button that stays pressed or not, for a view or filter option that applies at once.

- **Import:** `import { Toggle } from "@booleanpress/ui/toggle"`
- **Radix Toggle:** <https://www.radix-ui.com/primitives/docs/components/toggle>
- **APG Button (toggle button):** <https://www.w3.org/WAI/ARIA/apg/patterns/button/>
- **Page:** <https://ui.booleanpress.com/components/toggle> · @booleanpress/ui 0.1.0

## Usage

Name an icon-only toggle with `aria-label`; a toggle with text is named by its text. The name stays the same in both states: the pressed state is announced separately, so do not change the text to "Unpin" when it is pressed.

```tsx
import { StarIcon } from "lucide-react"
import { Toggle } from "@booleanpress/ui/toggle"

export function StarToggle() {
  return (
    <Toggle variant="outline" aria-label="Star">
      <StarIcon />
    </Toggle>
  )
}
```

It is uncontrolled with `defaultPressed`, or controlled with `pressed` and `onPressedChange`. `variant` is `default` (no border) or `outline`; `size` is `sm`, `default` or `lg`. `toggleVariants` gives the same classes to another element. To switch a setting on or off, use `Switch`; for several related toggles, `ToggleGroup`; for an action that is not a state, `Button`.

## Examples

### Basic

An icon toggle named by `aria-label`, and a text toggle that starts pressed.

```tsx
import { BoldIcon } from "lucide-react"
import { Toggle } from "@booleanpress/ui/toggle"

export default function ToggleBasic() {
  return (
    <div className="flex items-center gap-2">
      <Toggle aria-label="Bold">
        <BoldIcon />
      </Toggle>
      <Toggle defaultPressed>Pinned</Toggle>
    </div>
  )
}
```

### Variants and sizes

`default` and `outline`, each pressed, and the three sizes.

```tsx
import { StarIcon } from "lucide-react"
import { Toggle } from "@booleanpress/ui/toggle"

export default function ToggleVariants() {
  return (
    <div className="flex flex-col items-start gap-3">
      <div className="flex items-center gap-2">
        <Toggle aria-label="Star, default" defaultPressed>
          <StarIcon />
          Star
        </Toggle>
        <Toggle variant="outline" aria-label="Star, outline" defaultPressed>
          <StarIcon />
          Star
        </Toggle>
      </div>
      <div className="flex items-center gap-2">
        <Toggle variant="outline" size="sm">
          Small
        </Toggle>
        <Toggle variant="outline">Default</Toggle>
        <Toggle variant="outline" size="lg">
          Large
        </Toggle>
      </div>
    </div>
  )
}
```

### Controlled

`pressed` and `onPressedChange` keep the state in your code, here shown beside the toggle.

```tsx
import { useState } from "react"
import { BellIcon } from "lucide-react"
import { Toggle } from "@booleanpress/ui/toggle"

export default function ToggleControlled() {
  const [muted, setMuted] = useState(false)

  return (
    <div className="flex items-center gap-3">
      <Toggle variant="outline" pressed={muted} onPressedChange={setMuted}>
        <BellIcon />
        Mute alerts
      </Toggle>
      <p className="text-sm text-muted-foreground">{muted ? "Alerts are muted." : "Alerts are on."}</p>
    </div>
  )
}
```

### Disabled

A disabled toggle keeps its state, ignores clicks and keys, and leaves the tab order.

```tsx
import { ItalicIcon } from "lucide-react"
import { Toggle } from "@booleanpress/ui/toggle"

export default function ToggleDisabled() {
  return (
    <div className="flex items-center gap-2">
      <Toggle disabled aria-label="Italic">
        <ItalicIcon />
      </Toggle>
      <Toggle disabled defaultPressed variant="outline">
        Archived
      </Toggle>
    </div>
  )
}
```

## Accessibility

**Semantics.** A `button` with `aria-pressed` (`true` or `false`) and a `data-state` of `on` or `off`.

**Labels.** Icon-only toggles need `aria-label`. Keep the name constant across states.

**Focus.** It is in the tab order. The focus ring shows on keyboard focus, not on click.

**Known limits.**

- The small size is 32 px high and its minimum width is 32 px; the default is 36 px and the large 40 px. All meet WCAG 2.5.8's 24 px.
- The pressed state is shown by a fill (`--accent`). An icon-only toggle in the `default` variant has no border, so keep it next to a label or group when the fill alone would be missed.

### Keyboard

| Key | Behaviour |
| --- | --- |
| Space | Presses or releases the toggle. |
| Enter | Presses or releases the toggle. |

## API

### Toggle

Renders Radix Toggle.Root and passes it every other prop.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` |  | Render the child element instead, with this part's behaviour and classes merged onto it. |
| `defaultPressed` | `boolean` |  | The state of the toggle when initially rendered. Use `defaultPressed` if you do not need to control the state of the toggle. |
| `onPressedChange` | `((pressed: boolean) => void)` |  | The callback that fires when the state of the toggle changes. |
| `pressed` | `boolean` |  | The controlled state of the toggle. |

**Also exported:** `toggleVariants`, the class names of Toggle's variants and sizes (`cva`), to give another element the same look.

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="toggle"` (Toggle).

## Theming

The pressed fill is `--accent` with `--accent-foreground`; hover is `--muted`. The `outline` variant uses `--input` for its border.

| Token | Used for |
| --- | --- |
| `--accent` | background |
| `--accent-foreground` | text |
| `--destructive` | border, focus ring |
| `--input` | border |
| `--muted` | background |
| `--muted-foreground` | text |
| `--ring` | border, focus ring |
