ToggleButton
A two-state button (aria-pressed) — icon and/or text, on or off.
Last updated
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.
Show codeHide code
<ToggleButton iconOnly aria-label="Bold"><Bold /></ToggleButton>
<ToggleButton iconOnly pressed aria-label="Bold (pressed)"><Bold /></ToggleButton>Add
npx @ava-ui/apogee add toggle-button
No Apogee dependencies. Ships one scoped JS island (progressive enhancement).
Usage
---
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:
Show codeHide code
<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":
Show codeHide code
<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.
Show codeHide code
<ToggleButton pressed selectedColor="primary" selectedVariant="solid">Pressed</ToggleButton>
<Button color="primary">Button</Button>Show codeHide code
<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:
Show codeHide code
<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
Show codeHide code
<ToggleButton>Off</ToggleButton>
<ToggleButton pressed>On</ToggleButton>
<ToggleButton disabled>Disabled</ToggleButton>
<ToggleButton rounded pressed>Rounded</ToggleButton>- Pressed — the
selectedVariant×selectedColorfill (default a faded neutral). - Hover — the resting variant’s hover fill; pressed buttons use the selected fill’s highlighted step.
- Focus — visible
focus-visiblering (--border-primary). - Disabled — dimmed, not interactive.
- No JS — click does nothing; the button stays at its initial
pressedvalue.
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.
Show codeHide code
<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:
Show codeHide code
<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:
<ToggleButton manual iconOnly pressed={isBoldAtSelection} aria-label="Bold">
<Bold />
</ToggleButton>
Inside a ToggleButtonGroup 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">witharia-pressed— the native two-state pattern, full keyboard support for free. - Focus — a visible
focus-visiblering. - Icon-only usage (no visible text) always needs
aria-label. - No JS — the button is present and focusable but inert;
aria-pressednever 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-labelon every icon-onlyToggleButton. - Do use
manualwhen something other than a click drives the state (selection, external data) — don’t fight the self-toggle script. - Don’t use
ToggleButtonfor navigation or single-fire actions — that’sButton. 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-labelnames 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.