Accessibility pattern · Buttons and actions

Toggle button

A toggle button is an ordinary button with aria-pressed: its name says what it does, and the pressed state says whether it is on. The label never changes. The fill, a check mark and the icon show the state on screen; aria-pressed says it to assistive technology.

WCAG criteria
6
Keyboard rules
3
Checked with
axe, keyboard and the inspector

Live demo

Try it

Use it with a mouse, a keyboard or a screen reader. The inspector beside it shows what the browser tells assistive technology as you go: focus, state changes and announcements.

Saturday plans

Note · Shared with Meera

Pick up the cake from Lakshmi Bakery before six, and bring candles for Meera's birthday.

Family group

Sounds and banners pause. Messages still arrive.

The demo works without JavaScript only as far as its HTML does; the inspector needs JavaScript.

Keyboard

Keys it answers to

Every action works without a pointer.

KeyWhat it does
Tab or ShiftTabMoves between Bold, Italic, Underline, the note and Mute notifications.
Enter or SpaceSwitches the focused toggle on or off. The note takes or drops the style at once.
CtrlB or CtrlI or CtrlUIn the note, presses Bold, Italic or Underline without leaving the text; a status message says what changed.

Screen readers

What it announces

Written from the roles, names and states in the markup.

WhenExpected announcement
Focus reaches BoldBold, toggle button, not pressed
Space presses itPressed
Ctrl+I is pressed in the noteItalic on
Focus reaches the mute buttonMute notifications, toggle button, not pressed. Sounds and banners pause. Messages still arrive.
Enter presses itPressed (the name stays Mute notifications)

These are expected announcements, not recordings. Wording and order differ between screen readers and browsers.

Code

Copy the code

The exact files this demo runs on. The styles are served with a prefix that keeps this site's own styles out of the demo; what you copy is the original.

The markup

<div class="ap-toggle-button" data-ap-toggle-button>
  <div class="ap-toggle-button__top">
    <h3 class="ap-toggle-button__name" id="toggle-button-note-name">Saturday plans</h3>
    <p class="ap-toggle-button__meta">Note · Shared with Meera</p>
  </div>

  <div class="ap-toggle-button__strip" role="group" aria-label="Note style">
    <button type="button" class="ap-toggle-button__btn" aria-pressed="false" aria-keyshortcuts="Control+B" data-ap-style="bold">
      <svg class="ap-toggle-button__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M7 5h6a3.5 3.5 0 0 1 0 7H7Z"/><path d="M7 12h7a3.5 3.5 0 0 1 0 7H7Z"/></svg>
      <span>Bold</span>
      <svg class="ap-toggle-button__tick" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 12.5 4 4 8-9"/></svg>
    </button>
    <button type="button" class="ap-toggle-button__btn" aria-pressed="false" aria-keyshortcuts="Control+I" data-ap-style="italic">
      <svg class="ap-toggle-button__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M10 5h8M6 19h8M14 5l-4 14"/></svg>
      <span>Italic</span>
      <svg class="ap-toggle-button__tick" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 12.5 4 4 8-9"/></svg>
    </button>
    <button type="button" class="ap-toggle-button__btn" aria-pressed="false" aria-keyshortcuts="Control+U" data-ap-style="underline">
      <svg class="ap-toggle-button__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M7 4v7a5 5 0 0 0 10 0V4"/><path d="M5 20h14"/></svg>
      <span>Underline</span>
      <svg class="ap-toggle-button__tick" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="m6 12.5 4 4 8-9"/></svg>
    </button>
  </div>

  <div class="ap-toggle-button__note" id="toggle-button-note" role="textbox" aria-multiline="true" aria-labelledby="toggle-button-note-name" contenteditable="plaintext-only" spellcheck="false" data-ap-note>Pick up the cake from Lakshmi Bakery before six, and bring candles for Meera's birthday.</div>
  <p class="ap-toggle-button__sr" role="status" data-ap-status></p>

  <div class="ap-toggle-button__setting">
    <div class="ap-toggle-button__copy">
      <p class="ap-toggle-button__k">Family group</p>
      <p class="ap-toggle-button__hint" id="toggle-button-mute-hint">Sounds and banners pause. Messages still arrive.</p>
    </div>
    <button type="button" class="ap-toggle-button__btn ap-toggle-button__mute" aria-pressed="false" aria-describedby="toggle-button-mute-hint">
      <svg class="ap-toggle-button__icon ap-toggle-button__bell" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6 16v-5a6 6 0 0 1 12 0v5l1.5 2h-15Z"/><path d="M10 20.5a2 2 0 0 0 4 0"/></svg>
      <svg class="ap-toggle-button__icon ap-toggle-button__bell-off" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M8.6 5.6A6 6 0 0 1 18 11v4"/><path d="M6 11v5l-1.5 2H17"/><path d="M10 20.5a2 2 0 0 0 4 0"/><path d="m4 4 16 16"/></svg>
      <span>Mute notifications</span>
    </button>
  </div>
</div>

WCAG 2.2

What it meets

The success criteria this pattern takes care of, and how.

  • 1.4.1 Use of Color Level A

    A pressed button changes its fill, gains a check mark and an edge, and the mute bell is crossed out, so the state never rests on color.

  • 1.4.11 Non-text Contrast Level AA

    Each button's edge clears 3:1 against the card, and the pressed edge uses the accent, which also clears 3:1.

  • 2.1.1 Keyboard Level A

    Every toggle works with Enter and Space, and the formatting shortcuts work from inside the note.

  • 2.4.7 Focus Visible Level AA

    Each button shows a two-pixel focus ring, and the note shows one too while you type in it.

  • 4.1.2 Name, Role, Value Level A

    aria-pressed exposes each button as a toggle with its state; the names never change, so the state is never said twice.

  • 4.1.3 Status Messages Level AA

    A shortcut changes a button that does not have focus, so a status message says "Bold on" or "Bold off".

Usage

When to use it

Use it

  • Tool states that stay on until turned off, like bold text, a grid overlay, or mute.
  • A row of independent options where any number can be on at once.

Use something else

  • A setting that takes effect like a light switch, in a settings list: a switch reads more naturally there.
  • Choosing one option from several, like text alignment: use a radio group or a segmented control.
  • A button that opens or shows something: use aria-expanded, not aria-pressed.

Common failures

How it usually goes wrong

  • A label that changes instead of a state

    A button that reads "Mute" and then "Unmute" with no pressed state leaves people guessing whether the label is the state or the action. Keep one label and let aria-pressed say on or off.

  • A changing label and aria-pressed together

    "Unmute, toggle button, pressed" is a puzzle. Pick one model: a fixed label with aria-pressed, as here, or a changing label with no pressed state.

  • Pressed shown by a slightly darker fill

    A small change of shade is invisible to many people. Pressed buttons here also gain a check mark and a stronger edge, and the mute icon changes.

  • A styled div with a class for the state

    A div that toggles an "active" class says nothing to assistive technology. A button with aria-pressed is announced as a toggle button with its state.

  • Shortcuts that change things silently

    Ctrl+B in the note presses a button that does not have focus, so nothing is announced on its own. The status message here says what changed.

Notes

Building it

  • aria-pressed takes "true" or "false", or "mixed" when a style covers only part of a selection. Leave the attribute off entirely and the button is an ordinary one.
  • In a full editor the pressed state follows the text under the caret, and the strip belongs in a toolbar with one tab stop; see the toolbar pattern.
  • The note uses contenteditable="plaintext-only", so pasting cannot bring in formatting the buttons do not know about.
  • Mute could equally be a switch. Use toggle buttons for tool states and switches for settings, and stay consistent within one product.

Sources: WAI-ARIA Authoring Practices: Button · WAI-ARIA 1.2: aria-pressed

Checked with axe in light and dark themes, at desktop and phone widths, and by keyboard. Not yet tested with every screen reader and browser pair. Report a correction