# ToggleButtonGroup

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

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

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

Last updated October 15, 2026

Uses JavaScript

Roving tabindex (one tab stop; arrows and Home/End move focus), and for `type="single"` a click listener on each child button that un-presses its siblings, so only one stays pressed at a time.

**Without JS:** Each ToggleButton stays at its initial pressed state and doesn't respond to clicks (see ToggleButton), so single-select mutual exclusivity never kicks in either.

```astro
<ToggleButtonGroup type="single" aria-label="Text alignment">
<ToggleButton iconOnly pressed aria-label="Left"><AlignLeft /></ToggleButton>
<ToggleButton iconOnly aria-label="Center"><AlignCenter /></ToggleButton>
<ToggleButton iconOnly aria-label="Right"><AlignRight /></ToggleButton>
</ToggleButtonGroup>
```

## Add

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

Pulls in `ToggleButton`. Ships one scoped JS island (progressive enhancement).

## Usage

```astro
---
import ToggleButtonGroup from '@/components/apogee/ToggleButtonGroup/ToggleButtonGroup.astro'
import ToggleButton from '@/components/apogee/ToggleButton/ToggleButton.astro'
---
<ToggleButtonGroup type="single" aria-label="Text alignment">
  <ToggleButton iconOnly aria-label="Bold"><Bold /></ToggleButton>
  <ToggleButton iconOnly aria-label="Italic"><Italic /></ToggleButton>
</ToggleButtonGroup>
```

## Anatomy

-   **Group** — a `role="group"` wrapper (`data-type="single" | "multiple"`) holding the buttons in its default slot.
-   **ToggleButton** (×N) — each child is a plain [`ToggleButton`](https://apogee.avaui.dev/toggle-button); the group doesn’t render or own them directly, it just reads and (in `single` mode) coordinates their `aria-pressed`.

## API

`@composable` — exposed to Content Editors in AVA. Any other attribute passes through to the root `<div>`.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `'single' \| 'multiple'` | `'multiple'` | `single`: mutually exclusive (one or none pressed). `multiple`: each button toggles independently. |
| `disabled` | `boolean` | `false` | Disables every button inside (the root is a `<fieldset>`; native, no JS). |
| `aria-label` | `string` | — | Label for the group (e.g. “Text alignment”). |
| `class` | `string` | — | Extra classes on the root, merged last. |

## Variants

Both `type` values:

```astro
<ToggleButtonGroup type="single" aria-label="Alignment">
<ToggleButton iconOnly pressed aria-label="Left"><AlignLeft /></ToggleButton>
<ToggleButton iconOnly aria-label="Center"><AlignCenter /></ToggleButton>
<ToggleButton iconOnly aria-label="Right"><AlignRight /></ToggleButton>
</ToggleButtonGroup>
<ToggleButtonGroup type="multiple" aria-label="Formatting">
<ToggleButton iconOnly pressed aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly pressed aria-label="Italic"><Italic /></ToggleButton>
<ToggleButton iconOnly aria-label="Underline"><Underline /></ToggleButton>
</ToggleButtonGroup>
```

## States

-   **Single-select** — clicking a button un-presses every sibling; clicking the already-pressed one clears the group (each button still self-toggles).
-   **Multiple-select** — each button toggles independently, no coordination.
-   **No JS** — buttons render at their initial `pressed` values and don’t respond to clicks (see [`ToggleButton`](https://apogee.avaui.dev/toggle-button)).
-   **Disabled** — `disabled` on the group disables every button; a focused button lifts above its neighbours so its outline is never clipped.

```astro
<ToggleButtonGroup type="single" aria-label="Text alignment" disabled>
<ToggleButton iconOnly pressed aria-label="Left"><AlignLeft /></ToggleButton>
<ToggleButton iconOnly aria-label="Center"><AlignCenter /></ToggleButton>
<ToggleButton iconOnly aria-label="Right"><AlignRight /></ToggleButton>
</ToggleButtonGroup>
```

## In a form

Each button submits its `value` under its `name` while pressed; in `single` mode at most one does.

```astro
<form>
<ToggleButtonGroup type="single" aria-label="Alignment">
  <ToggleButton iconOnly name="align" value="left" pressed aria-label="Left"><AlignLeft /></ToggleButton>
  <ToggleButton iconOnly name="align" value="center" aria-label="Center"><AlignCenter /></ToggleButton>
  <ToggleButton iconOnly name="align" value="right" aria-label="Right"><AlignRight /></ToggleButton>
</ToggleButtonGroup>
</form>
```

## Examples

Mixed sizes pass straight through to each `ToggleButton`:

```astro
<ToggleButtonGroup type="single" aria-label="Size">
<ToggleButton iconOnly size="small" pressed aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly size="small" aria-label="Italic"><Italic /></ToggleButton>
</ToggleButtonGroup>
```

## Events

The group dispatches `apogee:toggle-group-change` (`detail: { value: string[] }`, bubbling) after any button toggles; values come from each button’s `value`, falling back to its index.

## Accessibility

| Key | Action |
| --- | --- |
| `Tab` | Moves focus into the group (one stop), on the pressed button or the first. |
| `←` `→` | Move focus between buttons, wrapping at the ends; swapped under `dir="rtl"`. |
| `Home` `End` | First / last button. |
| `Enter` `Space` | Toggle the focused button. |

-   In `single` mode clicking the pressed button clears the group (a toggle, not a radio). Use [SegmentedControl](https://apogee.avaui.dev/segmented-control) for exactly-one.
-   `role="group"` with `aria-label` names the whole bar for AT users; each child keeps its own `aria-pressed` and `aria-label`.
-   **Focus** — the focused button shows its visible `focus-visible` ring.
-   **No JS** — the group renders (a `role="group"` of plain buttons); with scripts off none of them toggle, in either mode.

## Tokens

| Token | Used for |
| --- | --- |
| `--border-neutral` / `--background-page` | Group container border + fill |
| (see [`ToggleButton`](https://apogee.avaui.dev/toggle-button)) | Individual button colors |

## Do / Don’t

-   **Do** always set `aria-label` on the group describing what it controls (“Text alignment”, “View”).
-   **Do** use `type="single"` for mutually-exclusive choices (alignment, view mode) and `type="multiple"` for independent toggles (bold/italic/underline).
-   **Don’t** mix unrelated actions in one group — every button should belong to the same setting.
-   **Don’t** forget each child `ToggleButton` still needs its own `aria-label` when icon-only.

## Content

-   See [`ToggleButton`](https://apogee.avaui.dev/toggle-button) content rules — each child button’s label/`aria-label` follows the same guidance.

## Related

[

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

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

SegmentedControlA row of connected, mutually exclusive options backed by native radios — pick one from a small, always-visible set.

](https://apogee.avaui.dev/docs/segmented-control)[

ButtonGroupJoins adjacent Buttons into a single control, collapsing the inner corners and overlapping borders.

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