Skip to content

Roles need their required states and properties

  • Rule: axe/aria-required-attr
  • Checks: Required ARIA attributes must be provided
  • WCAG: 4.1.2
  • Fix class: mechanical: A fix needs no authored text or visual decision.
  • People who use a screen reader or a braille display, most of them blind.

A custom checkbox, radio button, switch or checkable menu item has no aria-checked

Section titled “A custom checkbox, radio button, switch or checkable menu item has no aria-checked”

Set aria-checked to "true" or "false", or "mixed" for a partly checked checkbox, and update it from script whenever the state changes. A native <input type="checkbox"> or <input type="radio"> needs none of this.

<button type="button" role="switch" aria-checked="false">Wi-Fi</button>

A combobox has no aria-expanded, or no aria-controls while expanded

Section titled “A combobox has no aria-expanded, or no aria-controls while expanded”

Set aria-expanded to "false" while the popup is closed and "true" while it is open, and point aria-controls at the popup’s id.

<label for="city">City</label>
<input id="city" type="text" role="combobox" aria-expanded="false" aria-controls="city-options" aria-autocomplete="list">
<ul id="city-options" role="listbox" aria-label="Cities" hidden></ul>

A slider, scrollbar, meter or focusable separator has no aria-valuenow

Section titled “A slider, scrollbar, meter or focusable separator has no aria-valuenow”

Set aria-valuenow to the current number, with aria-valuemin and aria-valuemax for the range, and keep it in step as the value changes. Add aria-valuetext where the number alone means little, such as “Medium”; axe excuses a slider that has only aria-valuetext, but ARIA still requires aria-valuenow.

A <div> or <span> has role="heading" but no aria-level

Section titled “A <div> or <span> has role="heading" but no aria-level”

Use a native <h1> to <h6> instead. If the role must stay, add aria-level with the heading’s place in the outline.

Setting the attribute once and never updating it

Section titled “Setting the attribute once and never updating it”

axe checks only that the attribute is there, so people can hear a state that contradicts the screen, such as “not checked” on a switch that is on.

Changing the role to one with no required state, such as role="button" for a checkbox

Section titled “Changing the role to one with no required state, such as role="button" for a checkbox”

The rule passes, but people can no longer tell whether the option is on or off.

The part of the fix that differs in React, in its syntax. The rest of the guidance holds.

A state attribute is bound to a value that can be undefined, such as an optional prop

Section titled “A state attribute is bound to a value that can be undefined, such as an optional prop”

React leaves out an aria-* attribute whose value is undefined or null, so a switch whose state is not yet set renders with no aria-checked. Default the state to a boolean, such as useState(false) or checked = false among the props; React writes booleans as the "true" and "false" ARIA expects.

import { useState } from 'react';
function WifiSwitch() {
const [on, setOn] = useState(false);
return (
<button type="button" role="switch" aria-checked={on} onClick={() => setOn(!on)}>
Wi-Fi
</button>
);
}

Writing aria-checked={checked || undefined} to drop the attribute when it is false

Section titled “Writing aria-checked={checked || undefined} to drop the attribute when it is false”

React then removes aria-checked whenever the switch is off, so the role lacks its required state, and people never hear “off”.

axe-core’s documentation of the rule

CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.