WCAG 2.1 AA • AODA compliant • Updated 2025-10-28
What is this component? A radio button (<input type="radio">) allows users to select exactly one option from a list. All radio buttons within the same group share the same name attribute, ensuring that only one can be selected at a time.
Where and how is it used? Common use cases include:
| Requirement | WCAG Criteria | Description |
|---|---|---|
| Programmatic grouping | 1.3.1 Info and Relationships (A) | Use <fieldset> and <legend> to group related radios and provide a clear group name announced by screen readers. |
| Label association | 3.3.2 Labels or Instructions (A) | Each radio must have a clear, descriptive label associated via for/id or wrapping the input. |
| Keyboard operability | 2.1.1 Keyboard (A) | Users navigate between radios with arrow keys (↑/↓) and activate with Space. Focus moves with Tab between groups. |
| Focus visible | 2.4.7 Focus Visible (AA) | Provide a strong visual focus indicator around the selected or focused radio button. |
| Name, Role, Value | 4.1.2 Name, Role, Value (A) | Native radios expose role “radio,” name (from group legend/label), and state (checked/unchecked) automatically. |
| ARIA Role/Attribute | Used On | Purpose & Usage |
|---|---|---|
<input type="radio"> | Native radio | Preferred element. Implicit role “radio”; exposes state and supports keyboard navigation. |
role="radio" | Custom elements | Used only when recreating radios with div/spans. Must manage aria-checked, tabindex, arrow key navigation, and Space to toggle. |
aria-checked="true|false" | Custom radios | Conveys the selected state of each radio. Only one per group should be “true.” |
role="radiogroup" | Container for custom radios | Groups related radios so screen readers announce one selection among many. |
aria-labelledby / aria-label | Custom radios | Gives accessible name when visible text is not directly associated. |
<!-- Native radio buttons -->
<fieldset>
<legend>Preferred contact method</legend>
<label><input type="radio" name="contact" value="email"> Email</label>
<label><input type="radio" name="contact" value="phone"> Phone</label>
<label><input type="radio" name="contact" value="sms"> SMS</label>
</fieldset>
<!-- Custom radio group -->
<div role="radiogroup" aria-labelledby="rg1">
<p id="rg1">Payment Method</p>
<div role="radio" tabindex="0" aria-checked="true">Credit Card</div>
<div role="radio" tabindex="-1" aria-checked="false">PayPal</div>
</div>
<script>
const radios = document.querySelectorAll('[role="radio"]');
radios.forEach((r, i) => r.addEventListener('keydown', e => {
if(['ArrowDown','ArrowRight'].includes(e.key)) { e.preventDefault(); radios[(i+1)%radios.length].focus(); }
if(['ArrowUp','ArrowLeft'].includes(e.key)) { e.preventDefault(); radios[(i-1+radios.length)%radios.length].focus(); }
if(e.key===' '||e.key==='Spacebar') { radios.forEach(x=>x.setAttribute('aria-checked','false')); r.setAttribute('aria-checked','true'); }
}));
</script>
<input type="radio"> within <fieldset> and <legend> for semantic grouping.name attribute to logically group radios.checked) state per group.fieldset and legend; screen readers need a group name.name attribute causes multiple selections.aria-checked="true" in a group.| Test Item | WCAG Criteria | Pass Criteria |
|---|---|---|
| Group Name Announced | 1.3.1 Info and Relationships (A) | Screen reader announces legend or group label for radios. |
| Keyboard Navigation | 2.1.1 Keyboard (A) | Tab moves between groups; arrow keys move within group; Space selects option. |
| Focus Visible | 2.4.7 Focus Visible (AA) | Focus indicator clearly visible on focused radio. |
| State Announcement | 4.1.2 Name, Role, Value (A) | AT announces “selected” and “not selected” states correctly. |
| Single Selection | 3.3.2 Labels/Instructions (A) | Only one option per group can be selected at a time. |