You build a toggle that looks like a checkbox. You style two shipping options as buttons. Sighted users get it. Then you open VoiceOver.
On the custom toggle, from a real capture: Email notifications (custom) - and the hint: you are currently on a button, group. No checkbox role. No checked state. Beside it, the native control announces as a checkbox. Same job. Completely different exposure.
WCAG 4.1.2 Name, Role, Value says the role and value must be programmatically determinable. Native checkbox, radio, and fieldset give you that for free. A click-only div or plain buttons that fake selection fail 4.1.2 - missing roles, missing value.
Why this trips people up
Two failure patterns show up a lot:
-
Custom "checkbox" with no widget role - a clickable
divthat looks finished. In the accessibility tree you may only get StaticText. Tab can skip it entirely. -
Buttons faking a radio group - sibling buttons with no
radiogroupparent and no "1 of 2" / "2 of 2". They look like a choice group. VoiceOver hears two independent buttons.
What to do instead
- Prefer native checkbox / radio / fieldset when you can
- When you must build custom:
role="checkbox"+ focusability +aria-checked, orrole="radiogroup"withrole="radio"on each option - Extra ARIA is not the goal - the correct widget roles are. Then verify with a screen reader
Practice it
I published a walkthrough on A11Y in Practice with broken vs fixed paths on the same free lab:
- Video: https://www.youtube.com/watch?v=PJh5PMdxHwA
- Lab: https://wasexamprep.com/labs/custom-controls/control-roles
- Write-up: https://www.wasexamprep.com/blog/custom-div-is-not-a-checkbox
- Short: https://www.youtube.com/shorts/zz-ob1zySyY
Part of WAS Exam Prep / WAS Lab study notes (independent; not affiliated with IAAP or W3C).
Takeaway: a custom div that looks like a checkbox is not a checkbox until assistive tech gets the role and value - prefer native HTML when you can.
Top comments (0)