Accessibility pattern · Form inputs

Checkbox group with select all

A fieldset and legend name the group, so each option is announced with the question it answers. Select all is a native checkbox whose indeterminate state follows the options both ways, and a polite status says how many of them are chosen.

WCAG criteria
6
Keyboard rules
4
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.

Notifications

Sent to [email protected]

Email me about

Security alerts are always sent, so they are not listed.

3 selected out of 5

Shipping, delivery and returns.

When something on your wishlist gets cheaper.

A weekly round-up from makers you follow.

Festival sales and coupon codes.

Getting more from your account, once a month.

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
TabMoves to Select all, then through each option in turn; every checkbox is its own stop.
ShiftTabMoves back through the options.
SpaceOn Select all, ticks every option, or clears them all when all are ticked. On an option, ticks or clears it and Select all follows.
EnterOn Save preferences, saves the choices.

Screen readers

What it announces

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

WhenExpected announcement
Tab enters the groupEmail me about, group. Security alerts are always sent… Select all, checkbox, partly checked
Space on Select allChecked, then: 5 selected out of 5
Space clears one optionNot checked, then: 4 selected out of 5
Focus returns to Select allSelect all, checkbox, partly checked
With the option on, Space on Select all three timesChecked … Not checked … Partly checked, and your earlier picks are back
Save preferences is pressedPreferences saved

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

<form class="ap-checkbox-group" data-ap-checkbox-group novalidate aria-labelledby="cbg-form-name">
  <div class="ap-checkbox-group__top">
    <h3 class="ap-checkbox-group__name" id="cbg-form-name">Notifications</h3>
    <p class="ap-checkbox-group__sub">Sent to [email protected]</p>
  </div>

  <fieldset class="ap-checkbox-group__set" aria-describedby="cbg-hint">
    <legend class="ap-checkbox-group__legend">Email me about</legend>
    <p class="ap-checkbox-group__hint" id="cbg-hint">Security alerts are always sent, so they are not listed.</p>

    <div class="ap-checkbox-group__head">
      <div class="ap-checkbox-group__row ap-checkbox-group__row--all">
        <input class="ap-checkbox-group__box" type="checkbox" id="cbg-all" aria-controls="cbg-orders cbg-prices cbg-arrivals cbg-offers cbg-tips" />
        <div class="ap-checkbox-group__text">
          <label class="ap-checkbox-group__label" for="cbg-all">Select all</label>
        </div>
      </div>
      <p class="ap-checkbox-group__count" role="status" aria-atomic="true"><span class="ap-checkbox-group__n" data-ap-on>3</span> <span>selected out of</span> <span class="ap-checkbox-group__n" data-ap-total>5</span></p>
    </div>

    <div class="ap-checkbox-group__options">
      <div class="ap-checkbox-group__row">
        <input class="ap-checkbox-group__box" type="checkbox" id="cbg-orders" name="topics" value="orders" aria-describedby="cbg-orders-desc" checked />
        <div class="ap-checkbox-group__text">
          <label class="ap-checkbox-group__label" for="cbg-orders">Order updates</label>
          <p class="ap-checkbox-group__desc" id="cbg-orders-desc">Shipping, delivery and returns.</p>
        </div>
      </div>
      <div class="ap-checkbox-group__row">
        <input class="ap-checkbox-group__box" type="checkbox" id="cbg-prices" name="topics" value="prices" aria-describedby="cbg-prices-desc" checked />
        <div class="ap-checkbox-group__text">
          <label class="ap-checkbox-group__label" for="cbg-prices">Price drops</label>
          <p class="ap-checkbox-group__desc" id="cbg-prices-desc">When something on your wishlist gets cheaper.</p>
        </div>
      </div>
      <div class="ap-checkbox-group__row">
        <input class="ap-checkbox-group__box" type="checkbox" id="cbg-arrivals" name="topics" value="arrivals" aria-describedby="cbg-arrivals-desc" checked />
        <div class="ap-checkbox-group__text">
          <label class="ap-checkbox-group__label" for="cbg-arrivals">New arrivals</label>
          <p class="ap-checkbox-group__desc" id="cbg-arrivals-desc">A weekly round-up from makers you follow.</p>
        </div>
      </div>
      <div class="ap-checkbox-group__row">
        <input class="ap-checkbox-group__box" type="checkbox" id="cbg-offers" name="topics" value="offers" aria-describedby="cbg-offers-desc" />
        <div class="ap-checkbox-group__text">
          <label class="ap-checkbox-group__label" for="cbg-offers">Offers and sales</label>
          <p class="ap-checkbox-group__desc" id="cbg-offers-desc">Festival sales and coupon codes.</p>
        </div>
      </div>
      <div class="ap-checkbox-group__row">
        <input class="ap-checkbox-group__box" type="checkbox" id="cbg-tips" name="topics" value="tips" aria-describedby="cbg-tips-desc" />
        <div class="ap-checkbox-group__text">
          <label class="ap-checkbox-group__label" for="cbg-tips">Tips and guides</label>
          <p class="ap-checkbox-group__desc" id="cbg-tips-desc">Getting more from your account, once a month.</p>
        </div>
      </div>
    </div>
  </fieldset>

  <div class="ap-checkbox-group__foot">
    <p class="ap-checkbox-group__saved" role="status"></p>
    <button type="submit" class="ap-btn ap-btn--primary">Save preferences</button>
  </div>
</form>

WCAG 2.2

What it meets

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

  • 1.3.1 Info and Relationships Level A

    The fieldset and legend group the options under one question, and each option's description is joined with aria-describedby.

  • 1.4.1 Use of Color Level A

    Mixed is drawn as a bar, checked as a tick, and the count says the number in words, so nothing relies on color.

  • 1.4.11 Non-text Contrast Level AA

    Box edges, fills and the focus ring clear 3:1 against the card in both themes.

  • 2.5.8 Target Size (Minimum) Level AA

    Each label covers its whole row, at least 44 pixels high and the full width of the card.

  • 4.1.2 Name, Role, Value Level A

    Select all is a native checkbox; its indeterminate property is exposed as partly checked, and aria-controls lists the options it changes.

  • 4.1.3 Status Messages Level AA

    The count is a polite, atomic status, so a change is read as "4 selected out of 5" without moving focus.

Usage

When to use it

Use it

  • Several related options under one question, where people often want all or none.
  • Preference lists, filters and bulk selection in a table or a list of files.

Use something else

  • Two or three options: Select all adds a control without saving effort.
  • Options that exclude each other: use a radio group.

Common failures

How it usually goes wrong

  • Select all that only works one way

    If ticking every option by hand leaves Select all clear, the control lies about the state. It follows the options in both directions here.

  • Mixed shown as checked or unchecked

    A parent that looks ticked while two options are off hides what will be sent. It shows a bar and is announced as partly checked.

  • A group without a legend

    Without a fieldset and legend, "Price drops, checkbox" is heard with no question around it. The legend gives every option its context.

  • A count that is not atomic

    When only the number changes, many screen readers read just "4". aria-atomic=true makes the whole "4 selected out of 5" read.

  • Announcing the whole list on every change

    Reading every option's state after each tick is noise. One short count says what changed in total.

Notes

Building it

  • Select all decides from the options, not from its own checked property, so a click from mixed always ticks every option.
  • The option above follows the WAI-ARIA mixed checkbox example: from clear, Select all brings back the last partial choice instead of ticking everything.
  • The count's numbers sit in their own elements, so the words around them can be translated without rebuilding the sentence.
  • Leave Select all inside the fieldset, so it is announced with the group's name like any other option.

Sources: WAI-ARIA Authoring Practices: Checkbox · WAI-ARIA Authoring Practices: Mixed-state checkbox example · WAI Tutorials: Grouping controls

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