# ToggleButton

> A two-state button (aria-pressed) — icon and/or text, on or off.

URL: https://apogee.avaui.dev/docs/toggle-button
Last updated: 2026-10-15

A two-state button (aria-pressed) — icon and/or text, on or off.

Last updated October 15, 2026

Uses JavaScript

Wires a click listener that flips `aria-pressed` on every self-toggling (non-`manual`) `ToggleButton`.

**Without JS:** The button renders at its initial pressed state (from the pressed prop) and stays there — clicking it does nothing, since only the script flips aria-pressed.

```astro
<ToggleButton iconOnly aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly pressed aria-label="Bold (pressed)"><Bold /></ToggleButton>
```

## Add

```sh
npx @ava-ui/apogee add toggle-button
```

No Apogee dependencies. Ships one scoped JS island (progressive enhancement).

## Usage

```astro
---
import ToggleButton from '@/components/apogee/ToggleButton/ToggleButton.astro'
---
<ToggleButton aria-label="Bold"><Bold /></ToggleButton>
```

## API

`@composable` — exposed to Content Editors in AVA. Any other native `<button>` attribute (e.g. `disabled`) passes through.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `pressed` | `boolean` | `false` | Initial (and, in `manual` mode, current) pressed state. |
| `manual` | `boolean` | `false` | Don’t self-toggle on click — a parent (e.g. a rich-text toolbar) owns `aria-pressed`. |
| `value` | `string` | — | Identity inside a group and the submitted value. |
| `name` | `string` | — | With `value`, a hidden input submits the value while pressed, with or without JS. |
| `color` | `'primary' \| 'neutral' \| 'critical' \| 'positive' \| 'warning'` | `'neutral'` | Resting hue. |
| `variant` | `'solid' \| 'faded' \| 'outline' \| 'ghost'` | `'ghost'` | Resting fill style (the Button tables). |
| `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` | Height scale, same as Button. |
| `selectedColor` | `'primary' \| 'neutral' \| 'critical' \| 'positive' \| 'warning'` | `'neutral'` | Pressed hue. |
| `selectedVariant` | `'solid' \| 'faded'` | `'faded'` | Pressed fill style. |
| `rounded` | `boolean` | `false` | Fully round ends. |
| `iconOnly` | `boolean` | `false` | Square button for a lone icon; give it an `aria-label`. |
| `aria-label` | `string` | — | Label when the button is icon-only. |
| `class` | `string` | — | Extra classes, merged last. |

## Variants

Every resting `variant`, unpressed and pressed:

```astro
<ToggleButton variant="ghost">ghost</ToggleButton>
<ToggleButton variant="ghost" pressed>ghost</ToggleButton>
<ToggleButton variant="outline">outline</ToggleButton>
<ToggleButton variant="outline" pressed>outline</ToggleButton>
<ToggleButton variant="faded">faded</ToggleButton>
<ToggleButton variant="faded" pressed>faded</ToggleButton>
<ToggleButton variant="solid">solid</ToggleButton>
<ToggleButton variant="solid" pressed>solid</ToggleButton>
```

## Colours

Every resting `color` with `variant="outline"`:

```astro
<ToggleButton variant="outline" color="primary">primary</ToggleButton>
<ToggleButton variant="outline" color="neutral">neutral</ToggleButton>
<ToggleButton variant="outline" color="critical">critical</ToggleButton>
<ToggleButton variant="outline" color="positive">positive</ToggleButton>
<ToggleButton variant="outline" color="warning">warning</ToggleButton>
```

## Selected

`selectedColor` × `selectedVariant` style the pressed state; a solid primary pressed toggle matches a solid primary Button.

```astro
<ToggleButton pressed selectedColor="primary" selectedVariant="solid">Pressed</ToggleButton>
<Button color="primary">Button</Button>
```

```astro
<ToggleButton pressed selectedColor="primary" selectedVariant="faded">primary</ToggleButton>
<ToggleButton pressed selectedColor="neutral" selectedVariant="faded">neutral</ToggleButton>
<ToggleButton pressed selectedColor="critical" selectedVariant="faded">critical</ToggleButton>
<ToggleButton pressed selectedColor="positive" selectedVariant="faded">positive</ToggleButton>
<ToggleButton pressed selectedColor="warning" selectedVariant="faded">warning</ToggleButton>
<ToggleButton pressed selectedColor="primary" selectedVariant="solid">primary</ToggleButton>
<ToggleButton pressed selectedColor="neutral" selectedVariant="solid">neutral</ToggleButton>
<ToggleButton pressed selectedColor="critical" selectedVariant="solid">critical</ToggleButton>
<ToggleButton pressed selectedColor="positive" selectedVariant="solid">positive</ToggleButton>
<ToggleButton pressed selectedColor="warning" selectedVariant="solid">warning</ToggleButton>
```

## Sizes

Every size, icon-only:

```astro
<ToggleButton iconOnly size="small" aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly size="large" aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly size="xlarge" aria-label="Bold"><Bold /></ToggleButton>
```

## States

```astro
<ToggleButton>Off</ToggleButton>
<ToggleButton pressed>On</ToggleButton>
<ToggleButton disabled>Disabled</ToggleButton>
<ToggleButton rounded pressed>Rounded</ToggleButton>
```

-   **Pressed** — the `selectedVariant` × `selectedColor` fill (default a faded neutral).
-   **Hover** — the resting variant’s hover fill; pressed buttons use the selected fill’s highlighted step.
-   **Focus** — visible `focus-visible` ring (`--border-primary`).
-   **Disabled** — dimmed, not interactive.
-   **No JS** — click does nothing; the button stays at its initial `pressed` value.

## In a form

A hidden input submits `value` under `name` while pressed; nothing is submitted when unpressed, and a button with `name` but no `value` never submits.

```astro
<form>
<ToggleButton name="bold" value="1" pressed>Bold</ToggleButton>
<ToggleButton name="italic" value="1">Italic</ToggleButton>
<ToggleButton name="draft">No value</ToggleButton>
</form>
```

## Examples

Text-only and icon-plus-text:

```astro
<ToggleButton>Bold</ToggleButton>
<ToggleButton><Bold /> Bold</ToggleButton>
```

`manual` for a toolbar that reads external state (e.g. the current text selection) instead of toggling itself:

```astro
<ToggleButton manual iconOnly pressed={isBoldAtSelection} aria-label="Bold">
  <Bold />
</ToggleButton>
```

Inside a [ToggleButtonGroup](https://apogee.avaui.dev/toggle-button-group) the group handles single-select exclusivity and arrow-key focus.

## Events

Each button dispatches `apogee:toggle-change` (`detail: { pressed, value }`, bubbling) after it toggles.

## Accessibility

| Key | Action |
| --- | --- |
| `Tab` | Moves focus to the button. |
| `Enter` `Space` | Toggle the pressed state. |

-   Real `<button type="button">` with `aria-pressed` — the native two-state pattern, full keyboard support for free.
-   **Focus** — a visible `focus-visible` ring.
-   Icon-only usage (no visible text) always needs `aria-label`.
-   **No JS** — the button is present and focusable but inert; `aria-pressed` never changes. Don’t ship an icon-only, JS-dependent toggle as the only way to reach a piece of functionality.

## Tokens

| Token | Used for |
| --- | --- |
| `--foreground-neutral-faded` / `--foreground-neutral` | Unpressed vs. pressed label color |
| `--background-neutral-faded-highlighted` | Hover fill |
| `--background-neutral-faded` | Pressed fill |
| `--outline-color-focus` | Focus ring |

## Do / Don’t

-   **Do** pass `aria-label` on every icon-only `ToggleButton`.
-   **Do** use `manual` when something other than a click drives the state (selection, external data) — don’t fight the self-toggle script.
-   **Don’t** use `ToggleButton` for navigation or single-fire actions — that’s `Button`. A toggle must represent a persistent on/off state.
-   **Don’t** rely on color alone to show pressed — the fill + text-color pair already carries it; don’t strip that via `class`.

## Content

-   Icon-only: no visible label, `aria-label` names the action, not the state (“Bold”, not “Toggle bold”).
-   With text: a short noun or verb naming the setting (“Bold”, “Show grid”), not its state (“On”/“Off”) — the pressed style already shows that.

## Related

[

ButtonTriggers an action. Renders a <button>, or an <a> when href is set. Hue, fill and height are three separate props.

](https://apogee.avaui.dev/docs/button)[

ToggleButtonGroupConnects ToggleButtons into a segmented bar — single-select or multi-select.

](https://apogee.avaui.dev/docs/toggle-button-group)[

IconToggleTwo icons in one box that swap with a scale and fade, driven by a ToggleButton or your own state.

](https://apogee.avaui.dev/docs/icon-toggle)[

SwitchA toggle for an immediately-applied binary setting. A real checkbox under the hood, zero JS.

](https://apogee.avaui.dev/docs/switch)
