ToggleButton
ToggleButton switches between selected and unselected states to represent a persistent on/off choice. Use it standalone for binary actions like bold, mute, or favorite, or inside a ToggleButtonGroup for single-select or multi-select toolbar controls.
Installation
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
Requires React 19+, base CSS and a Theme provider. See Setup.
Toggle Button
ToggleButton switches between selected and unselected states to represent a persistent on/off choice. Use it standalone for binary actions like bold, mute, or favorite, or inside a ToggleButtonGroup for single-select or multi-select toolbar controls.
| Prop | Type | Default | Description |
|---|---|---|---|
| label * | string | Accessible label for the button. Used as visible text, or as aria-label for icon-only buttons. | |
| isPressed | boolean | Whether the button is currently pressed. Ignored for a ToggleButtonGroup member with a value. | |
| onPressedChange | (isPressed: boolean, event: MouseEvent) => void | Called synchronously when pressed state should change. Receives the next state and the click event; call event.preventDefault() to skip pressedChangeAction. Without an Action, the callback produces no Action-pending feedback. Ignored for a ToggleButtonGroup member with a value. | |
| pressedChangeAction | (isPressed: boolean) => void | Promise<void> | Action handler for API- or navigation-backed toggles, run in a transition after the synchronous onPressedChange callback unless that callback calls event.preventDefault(). Works without onPressedChange. Shows an optimistic pressed state and a spinner while pending, and remains interruptible by re-clicks. Omit it for callback-only toggles with no Action-pending feedback. Ignored for a ToggleButtonGroup member with a value. | |
| size | 'sm' | 'md' | 'lg' | 'md' | Button size. Defaults to group size when inside a group. |
| elevation | 'none' | 'low' | 'med' | 'high' | 'none' | Resting shadow depth for floating (FAB-style) toggle buttons, mirroring Button. none is the default flat button; low/med/high map to the shadow token scale. Applies inside a ToggleButtonGroup as well — grouped children retain their own elevation. |
| isDisabled | boolean | false | Whether the button is disabled. |
| isLoading | boolean | false | Whether the button shows a loading spinner. |
| icon | ReactNode | Icon element. When provided without children, button becomes icon-only with tooltip from label. | |
| isIconOnly | boolean | false | When true, renders as a square icon-only button with label as the aria-label and an automatic tooltip from the label. |
| pressedIcon | ReactNode | Icon shown when pressed. Falls back to icon if not provided. | |
| children | ReactNode | Visible content. If omitted with icon, button becomes icon-only. | |
| tooltip | string | Tooltip text shown on hover. | |
| value | string | Value identifier when used inside ToggleButtonGroup. Required in groups. | |
| data-testid | string | Test selector for automated testing frameworks. |
Usage guidance
- Use a filled or colored icon for the pressed state so users can see the current state at a glance: an outline star vs a solid star, for example.
- Keep the label identical between pressed and unpressed states. Let the visual treatment (icon, weight, background) communicate the change.
- Wrap related toggles in a ToggleButtonGroup with an accessible label so screen readers announce them as a connected set.
- Avoid: Don't use a ToggleButton for one-time actions like "Submit" or "Delete"; those are regular Buttons, not toggles.
- Avoid: Don't mix ToggleButtons with regular Buttons inside the same group; use only ToggleButtons in a ToggleButtonGroup.
- Avoid: Don't use a ToggleButton for on/off settings that persist across sessions; use a Switch instead, which better communicates "setting" semantics.
Theming API
{
"targets": [
{
"className": "astryx-toggle-button-group"
},
{
"className": "astryx-toggle-button",
"states": [
"isPressed"
],
"visualProps": [
"elevation"
]
}
]
}
ToggleButtonGroup
Accessibility
- Text label: The label must have at least 4.5:1 contrast with the button background when selected and unselected. For Hover and Pointer down, measure the final background after the overlay is applied. — 4.5:1
- Essential icon or spinner arc: An icon-only ToggleButton must have at least 3:1 contrast between its icon and button background. The moving spinner arc must also meet 3:1. An icon beside a visible label does not need its own check. — 3:1
- Selected state indicator: The selected background must reach 3:1 only when users need it to tell selected from unselected. Label weight or a changed icon can also show selection. — 3:1 if relied upon
- Visible control boundary: The button edge needs 3:1 contrast only when users need it to see the control. A visible label or icon can show the control instead. — 3:1 if needed
- Keyboard focus indicator: The focus outline must have at least 3:1 contrast with the area around the button. Check both selected and unselected states. — 3:1
- Disabled appearance: Disabled controls do not need to meet these contrast ratios. — Not required
Anatomy
- Icon: A leading icon that represents the toggle action, like a star for favorite or bold "B" for formatting.
- Pressed icon: An alternate icon shown when pressed: typically a filled version of the default icon to reinforce the active state.
- Label: The visible text or accessible name. For icon-only toggles, used as the aria-label and auto-tooltip.
- Spinner: Replaces the icon during async operations triggered by pressedChangeAction.
Toggle Button Group
Groups toggle buttons for exclusive (single) or multi-select behavior. Uses discriminated union on type for type-safe value/onChange.
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | ReactNode | ToggleButton children. Give each member a value; the group owns its pressed state and ignores its isPressed, onPressedChange, and pressedChangeAction props. Member activation calls the group’s onChange without starting a member Action transition. | |
| label * | string | Accessible label for the group (aria-label). | |
| type | 'single' | 'multiple' | 'single' | Selection mode. Single allows one active button, multiple allows many. |
| value * | string | null | string[] | Currently selected value(s). Type depends on selection mode. | |
| onChange * | (value: string | null | string[]) => void | Called when selection changes. | |
| orientation | 'horizontal' | 'vertical' | 'horizontal' | Layout direction of the button group. |
| size | 'sm' | 'md' | 'lg' | 'md' | Default size for buttons in the group. Individual buttons can override. |
| isDisabled | boolean | false | Whether all buttons in the group are disabled. |
| xstyle | StyleXStyles | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value. | |
| data-testid | string | Test selector for automated testing frameworks. |
Examples
- Toggle Button Group: ToggleButtonGroup manages a set of ToggleButtons with single-select or multi-select behavior for options like view modes or filters.
- Toggle Button:
- ToggleButton — Color: Toggle buttons with colored icons in the pressed state. Shows accent-colored toolbar formatting and semantic reaction colors (yellow star, red heart, blue bookmark).
- ToggleButton — Group: Toggle button groups in single-select and multi-select modes. Single selection acts as a view mode switcher; multiple selection forms a formatting toolbar.
- ToggleButtonGroup — Vertical: A vertically stacked ToggleButtonGroup using the vertical orientation, shown with both single-select and multi-select behavior, ideal for sidebar-style option lists and vertical toolbars.
- ToggleButton — Icon Swap: Icon-only toggle buttons that swap between outline and solid icons when pressed. Use for actions like favorite, bookmark, or mute where the icon itself communicates the state.
- ToggleButton — Label: Toggle buttons with visible text labels that show a font weight shift on press. Use when the icon alone is not enough to communicate the action.
- ToggleButton — States: Default, pressed, disabled, and loading states of a standalone toggle button. Shows how visual treatment changes across states.
Complete example dependencies
Some examples import optional icons. The per-example install command in the examples list identifies exactly what that example needs. For all examples on this page:
npm install @heroicons/react@2.2.0