aria-checked
Indicates the current "checked" state of checkboxes, radio buttons, and other widgets. Supports three values: true, false, and mixed (for indeterminate states).
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
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
trueThe element is checked/selected. For checkboxes: checked state. For radio buttons: selected. For switches: on.
falseThe element is not checked/selected. For checkboxes: unchecked. For radio buttons: not selected. For switches: 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.
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
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.

