checkbox
A checkable input that has three possible values: true, false, or mixed. Checkboxes allow users to select multiple options from a set.
Overview
The checkbox role indicates an interactive control that represents a binary choice (checked or unchecked) or a tri-state control (checked, unchecked, or mixed/indeterminate).
Unlike radio buttons, checkboxes are independent - multiple checkboxes can be checked simultaneously. The mixed state is typically used for parent checkboxes that control a group of child checkboxes.
Use Native <input type="checkbox">
Always prefer the native <input type="checkbox"> element. Only use role="checkbox" when you absolutely must use a different element for styling reasons.
Live Demo: Checkbox States
Single Checkbox
Mixed State (Select All)
Try with keyboard: Tab to focus checkboxes, press Space to toggle. Screen readers will announce the checkbox state (checked, not checked, or mixed).
Code Examples
Basic Checkbox
<!-- Basic Checkbox -->
<div
role="checkbox"
tabindex="0"
aria-checked="false"
onclick="toggleCheckbox(this)"
onkeydown="handleKeyPress(event, this)"
>
<span class="checkbox-icon">☐</span>
I agree to the terms
</div>
<script>
function toggleCheckbox(element) {
const isChecked = element.getAttribute('aria-checked') === 'true';
element.setAttribute('aria-checked', !isChecked);
// Update visual indicator
const icon = element.querySelector('.checkbox-icon');
icon.textContent = !isChecked ? '☑' : '☐';
}
function handleKeyPress(event, element) {
if (event.key === ' ' || event.key === 'Enter') {
event.preventDefault();
toggleCheckbox(element);
}
}
</script>Mixed State (Select All)
<!-- Mixed State Checkbox (Select All) -->
<div
role="checkbox"
tabindex="0"
aria-checked="mixed"
onclick="toggleSelectAll()"
id="select-all"
>
<span class="checkbox-icon">⊟</span>
Select All
</div>
<div role="group" aria-labelledby="select-all">
<div role="checkbox" aria-checked="true" tabindex="0">
Option 1
</div>
<div role="checkbox" aria-checked="false" tabindex="0">
Option 2
</div>
<div role="checkbox" aria-checked="true" tabindex="0">
Option 3
</div>
</div>
<script>
function toggleSelectAll() {
const parent = document.getElementById('select-all');
const state = parent.getAttribute('aria-checked');
const children = document.querySelectorAll('[role="group"] [role="checkbox"]');
// If mixed or unchecked, check all
// If checked, uncheck all
const newState = state === 'true' ? 'false' : 'true';
parent.setAttribute('aria-checked', newState);
children.forEach(child => {
child.setAttribute('aria-checked', newState);
});
updateIcon(parent, newState);
}
</script>Native HTML Checkbox
<!-- Native HTML Checkbox (Best Practice) -->
<label class="checkbox-label">
<input
type="checkbox"
name="subscribe"
checked
/>
Subscribe to newsletter
</label>
<!-- Styled Native Checkbox -->
<style>
.checkbox-label input[type="checkbox"] {
width: 20px;
height: 20px;
cursor: pointer;
accent-color: #4F46E5; /* Indigo */
}
</style>
<!-- Use native checkboxes whenever possible! -->React Components
// React Checkbox Component
import { useState } from 'react';
// Good: Use native checkbox
function NativeCheckbox({ label, checked, onChange }) {
return (
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={checked}
onChange={(e) => onChange(e.target.checked)}
className="w-5 h-5 text-indigo-600"
/>
<span>{label}</span>
</label>
);
}
// Custom checkbox with role (only when necessary)
function CustomCheckbox({ label, checked, onChange }) {
const handleKeyDown = (e) => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
onChange(!checked);
}
};
return (
<div
role="checkbox"
tabIndex={0}
aria-checked={checked}
onClick={() => onChange(!checked)}
onKeyDown={handleKeyDown}
className="flex items-center gap-2 cursor-pointer"
>
<div className={`w-5 h-5 border-2 rounded flex items-center justify-center ${
checked ? 'bg-indigo-600 border-indigo-600' : 'border-gray-400'
}`}>
{checked && (
<svg className="w-4 h-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
)}
</div>
<span>{label}</span>
</div>
);
}
// Mixed state checkbox (Select All)
function SelectAllCheckbox({ items, selectedItems, onSelectAll }) {
const allSelected = selectedItems.length === items.length;
const someSelected = selectedItems.length > 0 && selectedItems.length < items.length;
const checked = allSelected ? true : someSelected ? 'mixed' : false;
return (
<div
role="checkbox"
tabIndex={0}
aria-checked={checked}
onClick={onSelectAll}
className="flex items-center gap-2 cursor-pointer font-bold"
>
<div className="w-5 h-5 border-2 rounded flex items-center justify-center bg-indigo-600 border-indigo-600">
{checked === true && '✓'}
{checked === 'mixed' && '−'}
</div>
<span>Select All</span>
</div>
);
}Required Attributes
aria-checkedRequired. Indicates the checkbox state. Must be one of:
- •
"true"- Checkbox is checked - •
"false"- Checkbox is unchecked - •
"mixed"- Checkbox is in mixed/indeterminate state (typically for "Select All")
Keyboard Support
Primary method to toggle checkbox. For mixed state checkboxes, Space typically checks all children.
Best Practices
Use native <input type="checkbox"> whenever possible
Always provide a visible label for checkboxes
Make sure checkboxes have sufficient click/tap target size (44x44px minimum)
Use aria-checked="mixed" for parent checkboxes that control child checkboxes
Provide clear visual indicators for all three states (checked, unchecked, mixed)
Group related checkboxes with <fieldset> and <legend> or role="group"
Don't use checkbox for mutually exclusive options - use radio buttons
Don't forget to add tabindex="0" when using role="checkbox" on divs
Don't rely solely on color to indicate checkbox state
Don't make checkbox labels clickable separately - the entire control should be clickable
Common Use Cases
Accessibility Notes
Screen Reader Announcements
Screen readers will announce "checkbox, checked" or "checkbox, not checked" along with the label. For mixed state, it will announce "checkbox, mixed" or "checkbox, partially checked" depending on the screen reader.
Mixed State Usage
The mixed state should only be used for parent checkboxes when some (but not all) child checkboxes are checked. It indicates a partially selected state and is commonly used in "Select All" functionality.
Focus Management
Checkboxes should be keyboard focusable with tabindex="0". Provide clear visual focus indicators that meet WCAG contrast requirements. Native checkboxes handle this automatically.

