Accessible radio buttons and checkboxes
Updated October 14, 2026 · 6 min read
Radio buttons and checkboxes are among the simplest controls in HTML — and among the most often broken on shops, because designers want custom swatches, toggles and filter chips. This page shows how to keep them accessible. It is part of the accessible UI patterns series.
When to use which
| Radio buttons | Checkboxes |
|---|---|
| Exactly one choice from a set: delivery method, payment method, size | Each option on or off independently: filters, newsletter opt-in, gift wrap |
| One option is usually preselected | Any number can be checked, including none |
The APG describes a radio group as a set of checkable buttons where "no more than one of the buttons can be checked at a time", and checkboxes as dual-state (checked or not) or tri-state, with an extra partially checked state — for example "Select all" when only some items are selected.
Relevant WCAG criteria
- 1.3.1 Info and Relationships (A): the group and its question must be in the code, not only in the layout.
- 3.3.2 Labels or Instructions (A): every option has a label; the group has a question.
- 4.1.2 Name, Role, Value (A): role, name and checked state are exposed.
- 2.1.1 Keyboard (A) and 2.4.7 Focus Visible (AA).
- 1.4.11 Non-text Contrast (AA): the box or circle and the checked state need 3:1 contrast.
- 2.5.8 Target Size (Minimum) (AA): a clickable label makes the target larger.
- 3.2.2 On Input (A): selecting an option must not unexpectedly change the context, for example reload the page to another country.
Code example
<fieldset>
<legend>Delivery method</legend>
<input type="radio" id="ship-courier" name="shipping" value="courier" checked>
<label for="ship-courier">Courier, 1–2 days, €6.90</label>
<input type="radio" id="ship-locker" name="shipping" value="locker">
<label for="ship-locker">Parcel locker, 2–3 days, €3.90</label>
</fieldset>
<fieldset>
<legend>Colour</legend>
<!-- Swatches: the real input stays, only its look changes -->
<input type="radio" id="c-navy" name="colour" value="navy" class="swatch-input">
<label for="c-navy" class="swatch" style="--swatch:#1f2a44">Navy</label>
</fieldset>
<input type="checkbox" id="terms" name="terms" required>
<label for="terms">I accept the <a href="/en/terms">terms and conditions</a></label>
Notes:
fieldsetandlegendare W3C technique H71. It says they matter most for radio buttons and checkboxes that share a field name, and are not required when the individual labels already make the question clear.- For swatches, keep the text "Navy" in the label (visually hidden if needed), so the colour is not conveyed by colour alone.
- Visually hide the native input with a clip technique, never with
display: noneorvisibility: hidden, which remove it from keyboard and screen readers. Make the focus outline visible on the label when the input has focus. - If you build a custom control without a native input, the APG pattern uses
role="radiogroup"withrole="radio"children orrole="checkbox",aria-checked(true,falseand for checkboxesmixed), a label for the group, and full keyboard support written by you. The native input is far simpler.
Keyboard interaction
From the APG radio group and checkbox patterns:
| Key | Radio group | Checkbox |
|---|---|---|
| Tab / Shift + Tab | Moves focus into and out of the group (one tab stop for the group) | Moves to the next or previous checkbox |
| Arrow keys | Move to the next or previous option and check it, wrapping at the ends | — |
| Space | Checks the focused option if it is not checked | Toggles checked and not checked |
Inside a toolbar, the APG notes, arrow keys move focus without changing the checked option, and Space checks it.
Common mistakes on online shops
- Colour and size swatches built as
divorspanwith click handlers — no role, no keyboard, no state. - The native input hidden with
display: nonebehind a nicely styled label. - Filters as checkboxes that reload the page on every click without warning, moving focus to the top.
- Groups without a question: "Yes / No" radio buttons whose question is only a visual heading.
- A checked state shown only by a thin border colour change with low contrast, or by colour alone on swatches.
- "I accept the terms" text not connected to the checkbox, so clicking the text does nothing and the screen reader reads "checkbox, not checked" with no name.
- Tiny targets for checkboxes in filter lists on mobile.
What automated checks catch vs manual testing
| Automated (axe-core rule) | What it finds |
|---|---|
label |
Inputs without a label |
aria-toggle-field-name |
Custom role="checkbox", radio or switch without a name |
form-field-multiple-labels |
A field with more than one label |
aria-required-attr |
A custom checkbox or radio without aria-checked |
aria-required-parent |
ARIA roles that must be inside a specific parent role, used outside it |
nested-interactive |
A link or button inside a custom control |
color-contrast, target-size |
Low-contrast label text, targets that are too small |
All are cards in our rule library. Manual testing is needed for keyboard behaviour (arrow keys in groups, Space), visible focus on custom swatches, contrast of the box and checked state (1.4.11), whether every group has a clear question and whether selecting an option changes the context. The contrast checker helps with the non-text contrast.
Test a product page with the free one-page check. It is an automated check, not a full audit.
Frequently asked questions
When do I use radio buttons and when checkboxes?
Radio buttons when exactly one option of a group can be chosen, such as a delivery method. Checkboxes when each option can be on or off on its own, such as filters or "I accept the terms".
Do radio buttons need a fieldset and legend?
Usually yes. W3C technique H71 says fieldset and legend matter most for radio buttons and checkboxes that share one question, because the individual labels do not say what the question is. It is not required when the labels alone are clear.
How do keyboard users operate a radio group?
Tab moves into the group and out of it, the arrow keys move between options and select them, and Space selects the focused option. That is the behaviour of native radio buttons and of the APG radio group pattern.
Can I use custom-styled checkboxes?
Yes, if the real input stays in the page and only its appearance changes. Hiding the input with display none and drawing a div instead removes it from the keyboard and from screen readers.
Can an automated check test radio buttons and checkboxes?
It finds inputs without a label, custom checkboxes without a name and fields with several labels. It cannot tell whether a group has a clear question or whether a custom control works with the keyboard.
Sources
- W3C WAI, APG Radio Group pattern: https://www.w3.org/WAI/ARIA/apg/patterns/radio/
- W3C WAI, APG Checkbox pattern: https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/
- W3C, Technique H71 fieldset and legend: https://www.w3.org/WAI/WCAG22/Techniques/html/H71