Toggle Group
A set of two-state buttons that can be toggled on or off.
ToggleGroup is a row of toggle buttons that belong together (text formatting, alignment, a view switch). With type="multiple" (the default) every item toggles on its own. With type="single" at most one item is on: pressing an item releases the one that was on, and pressing the item that is on releases it.
Installation
$ bext ui add toggle-group
Copy and paste the following into src/components/ui/toggle-group.tsx.
This component is interactive. Make sure the shared island is loaded (see Islands). It reacts to data-ui="toggle-group" hooks.
Usage
Anatomy
<ToggleGroup type="single" ariaLabel="…"> ├── <ToggleGroupItem value="a" pressed>A</ToggleGroupItem> └── <ToggleGroupItem value="b">B</ToggleGroupItem>
API Reference
ToggleGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "single" | "multiple" | "multiple" | "multiple": items are independent. "single": at most one item is on. Written to data-type and enforced by the island. |
| ariaLabel | string | — | Accessible name of the group (aria-label). |
| ariaLabelledby | string | — | id of a visible element that names the group. |
| variant | "default" | "outline" | "default" | "outline" draws a border around each item. |
| spacing | 0 | 1 | 2 | 0 | Gap between items. With 0 the items are joined and share their corners. |
| size | "default" | "sm" | "lg" | "default" | Written to data-size on the group. |
| id | string | — | id of the group. |
| className | string | — | Extra classes on the group. |
ToggleGroupItem
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | — | Written to data-value on the button. |
| pressed | boolean | false | Initial state. In a single group, set it on one item only. |
| ariaLabel | string | — | aria-label of the button, for an icon-only item. |
| disabled | boolean | false | Disables the item: it is skipped by the arrow keys. |
| id | string | — | id of the button. |
| children | ReactNode | — | Content of the button. |
Data Attributes
Attributes you can target in custom CSS to override the style for each state.
| Attribute | On | Values | Description |
|---|---|---|---|
| data-ui="toggle-group" | ToggleGroup | data-type="single" | "multiple" | Scope of the group for the island. |
| data-ui="toggle" | ToggleGroupItem | data-value="…" | Clicking flips the item. |
| data-state | ToggleGroupItem | "on" | "off" | Current state. The pressed look is styled from it. |
| aria-pressed | ToggleGroupItem (multiple) | "true" | "false" | Same state, for assistive technologies. |
| aria-checked | ToggleGroupItem (single) | "true" | "false" | Replaces aria-pressed once the island has turned the group into a radio group. |
Keyboard
| Key | Action |
|---|---|
| Tab / Shift+Tab | Moves focus into the group (onto the item that is on, else the first one), then out of it. The group is a single tab stop. |
| Arrow Right / Arrow Down | Moves focus to the next item. Wraps to the first. Does not change the state. |
| Arrow Left / Arrow Up | Moves focus to the previous item. Wraps to the last. |
| Home / End | Moves focus to the first or last item. |
| Space / Enter | Presses or releases the focused item. |
Accessibility
The group renders role="group" and each item is a <button type="button"> with aria-pressed, kept in sync by the island. Name the group with ariaLabel or ariaLabelledby, and give icon-only items an ariaLabel.
With type="single" the island exposes the group as a radio set: the root becomes role="radiogroup" and the items role="radio" with aria-checked instead of aria-pressed. Unlike a RadioGroup, the arrow keys only move the focus, and the item that is on can be released, which leaves the group with no selection. When a value is always required, use RadioGroup.
The island keeps a roving tabindex on the items. Without JavaScript every item stays in the tab order and the group stays a plain role="group" of pressed buttons.
Examples
Single
Edit this component live in the bext playground. The PRISM + signals version is the bext-native idiom: fine-grained reactivity, no virtual DOM.
On This Page