Checkboxes — Accessibility Specification

WCAG 2.1 AA • AODA compliant • Updated April 12, 2025


About this element

A checkbox (<input type="checkbox">) allows users to select one or more options from a set. Each checkbox has a binary state (checked/unchecked); some designs also use an indeterminate visual state for “partially selected” sets.

Where and how is it used? Common use cases include:

Accessibility Requirements

This section outlines WCAG criteria, ARIA roles and attributes, keyboard accessibility, and focus management.

Requirement WCAG Success Criteria Description for Designers & Developers
Programmatic relationships 1.3.1 Info and Relationships (A) Each checkbox is associated with a visible label via <label for> / id or by wrapping the input inside the label. Related groups use <fieldset> and <legend>.
Labels or Instructions 3.3.2 Labels or Instructions (A) Provide clear, descriptive labels. Include help text or aria-describedby when additional guidance is required.
Keyboard Operability 2.1.1 Keyboard (A) Checkbox receives focus with Tab and toggles with Space. Focus order follows the visual reading order.
Focus Visible 2.4.7 Focus Visible (AA) Provide a clear visual focus indicator when the checkbox or its label is focused.
Name, Role, Value 4.1.2 Name, Role, Value (A) Native checkboxes expose role “checkbox,” name (from label), and state (checked true/false; optionally mixed for tri‑state custom controls).
Contrast & Target Size 1.4.3 Contrast (AA), 2.5.5 Target Size (AAA recommended) Ensure text and icon contrast ≥ 4.5:1 and provide a comfortable hit area (≈ 44×44 CSS px) by making the label clickable.

ARIA Roles and Attributes

ARIA Role/Attribute Used On Purpose & Usage
<input type="checkbox"> Native checkbox Preferred element. Implicit role “checkbox”; exposes checked state and supports keyboard by default.
role="checkbox" Custom element (<div>/<span>) Use only when native control cannot be used. Must manage tabindex="0", aria-checked, and keyboard handlers for Space.
aria-checked="true|false|mixed" Custom or tri‑state checkboxes Announces checked/unchecked/mixed state. Keep visual state in sync. For native inputs, use the DOM indeterminate property for mixed.
aria-labelledby / aria-label Icon‑only or complex labels Provides accessible name when the visible text is not directly associated.
aria-describedby Checkbox with help/error text Associates explanatory text (e.g., “We’ll only email important updates”).
<fieldset> / <legend> Groups of related checkboxes Conveys group relationship and provides a shared accessible name for the set.

Implementation Guidelines

Code Examples (HTML, ARIA, JS)

<!-- Native checkbox with label -->
<input type="checkbox" id="news" name="news">
<label for="news">Send me product updates</label>

<!-- Grouped checkboxes with fieldset/legend -->
<fieldset>
  <legend>Notification channels</legend>
  <label><input type="checkbox" name="ch" value="email"> Email</label>
  <label><input type="checkbox" name="ch" value="sms"> SMS</label>
  <label><input type="checkbox" name="ch" value="push"> Push notifications</label>
</fieldset>

<!-- "Select all" (tri‑state) -->
<label><input type="checkbox" id="all"> Select all</label>
<label><input type="checkbox" class="item"> Item 1</label>
<label><input type="checkbox" class="item"> Item 2</label>
<script>
  const all = document.getElementById('all');
  const items = Array.from(document.querySelectorAll('.item'));
  function syncAll() {
    const checked = items.filter(i => i.checked).length;
    all.indeterminate = checked > 0 && checked < items.length;
    all.checked = checked === items.length;
  }
  items.forEach(i => i.addEventListener('change', syncAll));
  all.addEventListener('change', () => items.forEach(i => i.checked = all.checked));
  syncAll();
</script>

<!-- Custom checkbox (only when native cannot be used) -->
<div role="checkbox" aria-checked="false" tabindex="0" id="cbxCustom">Email me weekly tips</div>
<script>
  const c = document.getElementById('cbxCustom');
  function toggle(e) {
    const v = c.getAttribute('aria-checked') === 'true';
    c.setAttribute('aria-checked', String(!v));
  }
  c.addEventListener('click', toggle);
  c.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); toggle(); } });
</script>

DO’s

Don’ts

Common Pitfalls

Testing Checklist

Test with NVDA/JAWS/VoiceOver and keyboard on Chrome/Firefox/Safari.

Test Item WCAG Criteria Pass Criteria
Label Programmatically Associated 1.3.1 Info and Relationships (A), 3.3.2 (A) Clicking the label toggles the checkbox; AT announces the label as the control’s name.
Keyboard Focus & Toggle 2.1.1 Keyboard (A), 2.4.7 Focus Visible (AA) Each checkbox is reachable by Tab; Space toggles state; focus indicator is visible.
Group Name 1.3.1 Info and Relationships (A) <fieldset>/<legend> announce the set name to AT for related options.
State Announcement 4.1.2 Name, Role, Value (A) Checked/unchecked (and mixed if applicable) is announced correctly when toggled.
Tri‑state Logic 4.1.2 (A) “Select all” becomes indeterminate when some but not all items are checked; becomes checked when all are checked; unchecked when none.
Contrast & Target Size 1.4.3 Contrast (AA) Text/icon contrast ≥ 4.5:1; label provides a ~44×44px click/tap target.