Skip to content

togglebutton

togglebutton declares that an element performs an action and retains a two-state condition — a favorite affordance, a bold control in a formatting bar, a filter chip that stays on. Without it the pressed condition exists only as a data-* attribute: a screen reader announces “Favorite, button” whether the item is favorited or not, so the one fact the control exists to convey is the one that is missing.

Status — React: Implemented • Web Components: Implemented • iOS: Not yet planned • Android: Not yet planned

Roles

Apply the following roles to elements:

RoleTypeElement
togglebuttonControlElement of type: container or type: glyph
labelPartElement of type: text or nested instance prop of type: string
descriptionPartElement of type: text or nested instance prop of type: string
indicatorPartElement of type: container or type: glyph

There is no togglebutton ARIA role. The emission is a button plus aria-pressed; role="togglebutton" is never emitted because it is not a valid ARIA value.

A glyph carrying the role is re-hosted inside the button rather than becoming it, adding one level to the markup.

States

The togglebutton role is typically applied in conjunction with the following states:

StateEffectClassify?
pressedaria-pressed, flipped by the wired handlerRecommended
disabledNatively disabled, enforced by the platformRecommended
hover / activeNative, on the button itselfRecommended, if the library styles it
focus / focus-visibleNative focus indicatorOptional — prefer the platform default

Read more about states in specs.

Specs

Component anatomy typically has elements and roles like:

anatomy:
root:
type: container
role: togglebutton
icon:
type: glyph
role: indicator

Figma

Annotate the following layers:

  • root as role:togglebutton — on the component node
  • decorative glyphs as role:indicator

Classify the retained prop as pressed in the states convention, not as active. A toggle whose prop is classified active emits a pseudo-class and no aria-pressed.

React

Implementation

<FavoriteButton
pressed={isFavorite}
onPressedChange={setIsFavorite}
accessibilityLabel="Favorite"
/>

Before / After

// before
<div className="favorite" data-element="root" data-pressed={p.pressed}>
{/* … */}
</div>
// after
<button
type="button"
className="favorite"
data-element="root"
aria-pressed={isPressed}
aria-label={p.accessibleName}
disabled={p.disabled}
onClick={() => { setPressed(!isPressed); p.onPressedChange?.(!isPressed); p.onClick?.(); }}
>
{/* … */}
</button>

Contract

PropTypeTierGenerated body
onPressedChange?(pressed: boolean) => voidMUSTWired — flips pressed, then calls the prop
onClick?(e: MouseEvent) => voidSHOULDStub, called after the toggle
pressedboolean—The existing variant prop
onFocus? / onBlur?(e: FocusEvent) => voidCOULDForwarded to the element

Web Components

As button — a real <button> inside the shadow root with delegatesFocus, not host semantics. One addition: aria-pressed is written through a tri-state host setter, because removing the attribute when false would make the toggle announce as a plain button. It is removed only on null.

iOS

Not yet planned. Intended binding:

TypeToggle with .toggleStyle(.button)
AnnouncedThe on/off value; double-tap flips it

Android

Not yet planned. Intended binding:

TypeIconToggleButton with Role.Switch
AnnouncedThe checked value; double-tap toggles

Additional details

The two meanings of “pressed”

ConceptBound toEmitted asOwned by
activeThe momentary prop — the condition while a pointer is held down:activebutton
pressedThe retained prop — the condition the control keepsaria-pressedtogglebutton

A component with both declares two state entries against two different Figma props. The role decides which one reaches ARIA and which stays a pseudo-class.

Wired state

onPressedChange follows the wired state model: internal state seeded from the pressed prop, flipped on activation, then the consumer callback. The toggle works before a consumer attaches anything.

Prerequisite: a pressed classification in the states convention naming the prop. Without it the handler degrades to a stub and warns.

The existing pressed variant prop is the value source — no defaultPressed companion is emitted.

Accessible name

SourceResult
Text the button contains, or a label partNames it
A prop nominated as the accessible-name sourceEmitted as aria-label — the usual case, since toggles are often icon-only
Nothing resolvesWarns

See also

  • button — the same emission without the retained state
  • switch — on/off with immediate effect, announced as a switch
  • indicator — the decorative glyph a toggle usually contains
  • Roles overview — the vocabulary and how roles are authored