Who it affects
Someone using a screen reader hears a checkbox announced as checked while the box on screen is empty, ticks it to be sure, and has now unticked the thing they wanted.
Someone using a braille display reads a state code that disagrees with what the form will actually submit.
Someone navigating a data table by row hears level and position numbers that mean nothing because the table is not a tree.
Broken versus fixed
Both examples are live and rendered in a sandboxed frame. The broken one is deliberately broken.
The box is empty on screen. Depending on the browser and screen reader, it is announced as checked, or as not checked, or flips between the two once it is clicked. The form will submit it as unticked.
Announced: Remember me on this device, checkbox, not checked. The native input reports its own state and it always matches the screen.
Show the markup behind both examples
<style>body{font-family:system-ui;padding:16px;margin:0}label{display:flex;align-items:center;gap:8px}input{width:18px;height:18px}</style>
<label>
<input type="checkbox" name="remember" aria-checked="true">
Remember me on this device
</label>
<style>body{font-family:system-ui;padding:16px;margin:0}label{display:flex;align-items:center;gap:8px}input{width:18px;height:18px}</style>
<label>
<input type="checkbox" name="remember">
Remember me on this device
</label>
Test it by hand in two minutes
- Search the page source for aria-checked. For each hit on an input type=checkbox or radio, remove it and see if anything breaks; it usually does not.
- Tick the box with the mouse and inspect it. If aria-checked still says the old value, the attribute is not being kept in sync.
- Turn on VoiceOver or NVDA, Tab to the checkbox and press Space. The state you hear should change each time and match the box on screen.
- For aria-level, aria-posinset or aria-setsize on a row, check the nearest ancestor role. If it is table or grid rather than treegrid, the attributes do nothing useful.
The fix
<input type="checkbox" name="remember" aria-checked="true">
<input type="checkbox" name="remember" checked>
A native checkbox already tells assistive technology whether it is ticked. Use the checked attribute (or the checked property in script) and drop aria-checked altogether.
Tree attributes on a row in an ordinary grid
<div role="grid">
<div role="row" aria-level="2" aria-posinset="1" aria-setsize="4">...</div>
</div>
<div role="grid">
<div role="row">...</div>
</div>
aria-level, aria-posinset and aria-setsize on a row are only meaningful inside role="treegrid". In a plain grid, remove them.
Rows that really do form a tree
<div role="grid">
<div role="row" aria-level="2">...</div>
</div>
<div role="treegrid">
<div role="row" aria-level="2" aria-expanded="false">...</div>
</div>
If the rows genuinely nest and expand, the container should be a treegrid, and then the level attributes are correct.
Common mistakes
- Keeping aria-checked on a native checkbox and trying to update it in script to match, which only works until someone forgets.
- Setting aria-checked="false" on a box that has the checked attribute, hoping to show it as unticked.
- Adding aria-level to table rows to indent them visually; use CSS for that.
- Changing the input to a div with role="checkbox" so the attribute is allowed, which throws away the free keyboard support.
WCAG criteria this rule maps to
axe-core tags this rule as WCAG 2.2 level A. It is one of the checks for:
- 4.1.2 Name, Role, Value (level A). Official W3C text.
The scanner can see aria-checked on a native checkbox that disagrees with its checked state, and tree attributes on a row outside a treegrid. It cannot see state that goes wrong later in script, so test the control by hand after interacting with it. Automated tools find only part of the accessibility problems on a page; a pass on this rule is not a pass on the criterion.
Related rules
aria-allowed-attr
Every ARIA role comes with a short list of attributes it understands.
A
ARIA attributes must have valid values
aria-valid-attr-value
Each ARIA attribute accepts a particular kind of value: true or false, a number, a word from a short list, or the id of another element.
A
ARIA roles that need a particular parent must sit inside it
aria-required-parent
Some roles only make sense inside a particular parent.
A
ARIA toggle fields must have an accessible name
aria-toggle-field-name
Elements with role="switch", role="checkbox", role="radio", role="menuitemcheckbox" or role="menuitemradio" are toggles: they are on or off, checked or not.