Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTEWidget Attributes

aria-checked

Indicates the current "checked" state of checkboxes, radio buttons, and other widgets. Supports three values: true, false, and mixed (for indeterminate states).

Value Type
true | false | mixed
Used With
checkbox, radio, switch
Related
aria-pressed

Overview

The aria-checked attribute indicates the current "checked" state of checkboxes, radio buttons, and similar widgets. It's essential for creating accessible custom checkbox and radio button implementations.

While native <input type="checkbox"> and <input type="radio"> elements manage their checked state automatically, custom implementations using role="checkbox" or role="radio" require aria-checked.

The "mixed" State

The mixed value (also called indeterminate) is used for checkboxes when some but not all child checkboxes are selected. This is common in "Select All" scenarios. Note: role="switch" and role="radio" cannot have mixed state.

Live Demo: aria-checked in Action

Custom Checkbox (with mixed state)

aria-checked="false" (click to cycle through states)

Custom Switch

role="switch" aria-checked="false"

Custom Radio Group

Choose a color

role="radio" with aria-checked on selected option

Screen reader announcements: Checkbox: "Select all items, checkbox, partially checked". Switch: "Dark Mode, switch, on". Radio: "Blue, radio button, 2 of 3, checked".

Attribute Values

true

The element is checked/selected. For checkboxes: checked state. For radio buttons: selected. For switches: on.

Screen reader: "checked" or "selected" or "on"
false

The element is not checked/selected. For checkboxes: unchecked. For radio buttons: not selected. For switches: off.

Screen reader: "not checked" or "not selected" or "off"
mixed(checkbox only)

The element represents a partially checked state. Only valid for role="checkbox". Used when a parent checkbox has some but not all children checked.

Screen reader: "partially checked" or "mixed"

Code Examples

Basic Checkbox

<!-- Basic aria-checked usage -->

<!-- Checkbox: true/false -->
<div 
  role="checkbox" 
  aria-checked="false"
  tabindex="0"
  aria-label="Accept terms"
>
  ☐ Accept terms and conditions
</div>

<!-- Checkbox: checked -->
<div 
  role="checkbox" 
  aria-checked="true"
  tabindex="0"
  aria-label="Newsletter subscription"
>
  ☑ Subscribe to newsletter
</div>

<!-- Checkbox: mixed/indeterminate -->
<div 
  role="checkbox" 
  aria-checked="mixed"
  tabindex="0"
  aria-label="Select all items"
>
  ▣ Select all (2 of 5 selected)
</div>

Radio Group

<!-- Radio Group Pattern -->
<div role="radiogroup" aria-labelledby="group-label">
  <span id="group-label">Choose a color:</span>
  
  <div 
    role="radio" 
    aria-checked="true"
    tabindex="0"
  >
    ● Red
  </div>
  
  <div 
    role="radio" 
    aria-checked="false"
    tabindex="-1"
  >
    ○ Blue
  </div>
  
  <div 
    role="radio" 
    aria-checked="false"
    tabindex="-1"
  >
    ○ Green
  </div>
</div>

<!-- Note: Only selected radio has tabindex="0" -->
<!-- Arrow keys navigate between radios -->

Switch/Toggle

<!-- Switch/Toggle Pattern -->
<button 
  role="switch" 
  aria-checked="false"
  aria-label="Dark mode"
>
  <span aria-hidden="true">🌙</span>
  Dark Mode: Off
</button>

<!-- When toggled on -->
<button 
  role="switch" 
  aria-checked="true"
  aria-label="Dark mode"
>
  <span aria-hidden="true">☀️</span>
  Dark Mode: On
</button>

<!-- Note: Switches only use true/false, never "mixed" -->

Indeterminate State

<!-- Indeterminate/Mixed State -->
<!-- Used when a parent checkbox controls child checkboxes -->

<div class="checkbox-group">
  <!-- Parent checkbox -->
  <div 
    role="checkbox" 
    aria-checked="mixed"
    tabindex="0"
    aria-controls="child-1 child-2 child-3"
    id="parent-checkbox"
  >
    Select All Toppings
  </div>
  
  <!-- Child checkboxes -->
  <div class="children" style="margin-left: 20px;">
    <div 
      role="checkbox" 
      aria-checked="true"
      tabindex="0"
      id="child-1"
    >
      ☑ Cheese
    </div>
    <div 
      role="checkbox" 
      aria-checked="false"
      tabindex="0"
      id="child-2"
    >
      ☐ Pepperoni
    </div>
    <div 
      role="checkbox" 
      aria-checked="true"
      tabindex="0"
      id="child-3"
    >
      ☑ Mushrooms
    </div>
  </div>
</div>

<!-- Parent shows "mixed" because some but not all children are checked -->

React Components

// React Custom Checkbox Component
import { useState } from 'react';

function Checkbox({ label, checked, onChange, id }) {
  const handleKeyDown = (e) => {
    if (e.key === ' ' || e.key === 'Enter') {
      e.preventDefault();
      onChange(!checked);
    }
  };

  return (
    <div
      role="checkbox"
      aria-checked={checked}
      tabIndex={0}
      onClick={() => onChange(!checked)}
      onKeyDown={handleKeyDown}
      id={id}
      className={`checkbox ${checked ? 'checked' : ''}`}
    >
      <span aria-hidden="true">{checked ? '☑' : '☐'}</span>
      {label}
    </div>
  );
}

// Custom Switch Component
function Switch({ label, checked, onChange }) {
  const handleKeyDown = (e) => {
    if (e.key === ' ' || e.key === 'Enter') {
      e.preventDefault();
      onChange(!checked);
    }
  };

  return (
    <button
      role="switch"
      aria-checked={checked}
      onClick={() => onChange(!checked)}
      onKeyDown={handleKeyDown}
      className={`switch ${checked ? 'on' : 'off'}`}
    >
      <span className="switch-label">{label}</span>
      <span className="switch-track" aria-hidden="true">
        <span className="switch-thumb" />
      </span>
    </button>
  );
}

// Radio Group with proper arrow key navigation
function RadioGroup({ name, options, value, onChange }) {
  const handleKeyDown = (e, index) => {
    let newIndex = index;
    
    if (e.key === 'ArrowDown' || e.key === 'ArrowRight') {
      e.preventDefault();
      newIndex = (index + 1) % options.length;
    } else if (e.key === 'ArrowUp' || e.key === 'ArrowLeft') {
      e.preventDefault();
      newIndex = (index - 1 + options.length) % options.length;
    }
    
    if (newIndex !== index) {
      onChange(options[newIndex].value);
      // Focus the new option
      document.getElementById(`${name}-${newIndex}`)?.focus();
    }
  };

  return (
    <div role="radiogroup" aria-labelledby={`${name}-label`}>
      {options.map((option, index) => (
        <div
          key={option.value}
          id={`${name}-${index}`}
          role="radio"
          aria-checked={value === option.value}
          tabIndex={value === option.value ? 0 : -1}
          onClick={() => onChange(option.value)}
          onKeyDown={(e) => handleKeyDown(e, index)}
          className="radio-option"
        >
          <span aria-hidden="true">
            {value === option.value ? '●' : '○'}
          </span>
          {option.label}
        </div>
      ))}
    </div>
  );
}

Best Practices

Prefer native HTML checkbox/radio inputs when possible—they handle aria-checked automatically

Update aria-checked dynamically when state changes via user interaction

Support both Space and Enter keys for toggling checkboxes and switches

Use roving tabindex for radio groups (only selected option has tabindex="0")

Implement arrow key navigation for radio groups

Provide clear visual indication of checked, unchecked, and mixed states

×

Don't use aria-checked="mixed" on radio buttons or switches

×

Don't forget to include tabindex for keyboard accessibility

×

Don't use aria-checked without the appropriate role (checkbox, radio, switch, menuitemcheckbox, etc.)

×

Don't forget to handle keyboard events—click alone isn't accessible

Common Use Cases

Custom styled checkboxes
Toggle switches (dark mode, etc.)
Custom radio button groups
"Select all" with child checkboxes
Menu item checkboxes
Tree view item selection
Multi-select list items
Feature toggles in settings

Accessibility Notes

Keyboard Requirements

Checkboxes and switches must respond to Space (and optionally Enter) to toggle. Radio buttons use arrow keys to move selection within the group. Only the currently selected radio should have tabindex="0"; others should have tabindex="-1".

Screen Reader Announcements

Screen readers announce the checked state along with the role. For example: "Subscribe to newsletter, checkbox, not checked" or "Dark mode, switch, off". The announcement varies slightly between screen readers but conveys the same information.

Mixed State Behavior

When a checkbox has aria-checked="mixed", clicking it typically selects all children (transitions to "true"). Clicking again deselects all (transitions to "false"). The mixed state usually only appears when children are in different states.

Related Attributes

Specifications & Resources