Forms & Input

Checkbox

Available

An on/off selection control. Also covers handling the indeterminate (mixed) state.

What is a Checkbox?

A checkbox is a UI control that lets users toggle an item "on" or "off." It is commonly used for enabling settings, agreeing to terms of service, and selecting multiple items (notification types, filter criteria, etc.).

A "select all" parent checkbox introduces an additional complexity: beyond "all on" and "all off," it must represent a third state (indeterminate / mixed) when only some children are checked. This is a frequently overlooked detail.

Why Does Accessibility Matter?

The shortest path is to use a native <input type="checkbox">associated with a <label>. For the mixed state, use the indeterminate property.

Live Demo (Recommended Implementation)

Below is a "Select all" checkbox with three child checkboxes. When only some children are checked, the parent enters theindeterminate state (neither fully checked nor empty in appearance).

Accessible Checkbox (with Select All)
Notifications to receive

Try it: Tab to each checkbox → Space to toggle on/off. Check only one child and the parent becomes indeterminate; check all children and the parent also becomes checked.

Tip

In the indeterminate state, screen readers announce the "Select all" checkbox as "partially checked / mixed." This happens because settingindeterminate = true causes the browser to automatically exposearia-checked="mixed" to assistive technologies.

Keyboard Interaction

KeyActionPriority
TabMove focus to the next checkboxRequired
SpaceToggle the focused checkbox on/offRequired

Note

Toggling with Space is automatically provided simply by using <input type="checkbox">. There is no need to write custom key handling.

Required WAI-ARIA Roles and Properties

TargetAttribute / RoleMeaning
Input element<input type="checkbox">Native checkbox role, state, and keyboard interaction are all provided automatically.
Label<label> (wrapping the input or associated via for)Serves as the accessible name for the checkbox. Clicking the label also toggles the state.
Parent (select all)indeterminate = true (JS property)Represents the mixed (indeterminate) state when only some children are selected. Automatically conveyed as aria-checked="mixed".
Group<fieldset> + <legend>Groups related checkboxes and provides a group name (optional but recommended).

Note

indeterminate is a JavaScript property, not an HTML attribute(el.indeterminate = true). Even when the visual appearance shows the mixed state, the underlying checked value remains true or false, so be careful when handling form submission values.

Implementation: Recommended Pattern (Good)

Good / Recommended

Use a native <input type="checkbox"> associated with a <label>, and represent the parent's mixed state with indeterminate.

Markup:

<fieldset>
  <legend>Notifications to receive</legend>

  <!-- Parent: select all (intermediate state expressed via indeterminate) -->
  <label>
    <input type="checkbox" id="cb-all"> Select all
  </label>

  <!-- Children: native input + label association (toggle with Space) -->
  <label><input type="checkbox" class="cb-child"> Email</label>
  <label><input type="checkbox" class="cb-child"> SMS</label>
  <label><input type="checkbox" class="cb-child"> Push notifications</label>
</fieldset>

Syncing "Select all" and the indeterminate state:

const all = document.getElementById('cb-all');
const children = document.querySelectorAll('.cb-child');

// Parent -> children: toggle all at once
all.addEventListener('change', () => {
  children.forEach((c) => { c.checked = all.checked; });
  all.indeterminate = false; // All are now in sync, so clear the mixed state
});

// Children -> parent: update parent based on all / some / none checked
children.forEach((c) => c.addEventListener('change', () => {
  const checked = [...children].filter((x) => x.checked).length;
  all.checked = checked === children.length;                     // all -> on
  all.indeterminate = checked > 0 && checked < children.length;  // some -> mixed
}));

Anti-pattern (Bad)

Below is a "visual-only" checkbox built with <div> elements.It toggles with a mouse click, but cannot be operated with a keyboard, and its state is not announced.

Broken checkbox built with divs
Email
SMS
Push notifications

Try it: pressing Tab does not move focus to the checkboxes. Screen readers do not announce them as 'checkbox' or convey their on/off state.

<!-- ❌ Anti-pattern: a "checkbox-like" control built with a div -->
<div class="checkbox" onclick="this.classList.toggle('on')">
  <span class="box"></span> Email
</div>

Bad / Avoid

Issues with this implementation:

  • Not keyboard accessible — a div cannot receive focus.
  • Role is not conveyed — it is not recognized as a "checkbox."
  • State is not conveyed — the on/off (or indeterminate) state is not announced.
  • No label association — clicking the text does not toggle the control, and no accessible name is provided.

Implementation Checklist


Source (English):Checkbox Pattern — W3C APG(opens in a new tab)