ARIA attributes need valid values
- Rule:
axe/aria-valid-attr-value - Checks: ARIA attributes must conform to valid values
- WCAG: 4.1.2
- Fix class:
mechanical: A fix needs no authored text or visual decision.
Who it affects
Section titled “Who it affects”- People who use a screen reader or a braille display, most of them blind.
How to fix it
Section titled “How to fix it”An attribute has a value ARIA does not define, such as aria-expanded="yes", aria-live="true" or aria-haspopup="yes"
Section titled “An attribute has a value ARIA does not define, such as aria-expanded="yes", aria-live="true" or aria-haspopup="yes"”Use one of the values defined for that attribute: "true" or "false" for aria-expanded, with "mixed" also allowed for aria-checked and aria-pressed; "polite", "assertive" or "off" for aria-live; "false", "true", "menu", "listbox", "tree", "grid" or "dialog" for aria-haspopup. Where script writes a missing value as null or an empty string, write a real value or remove the attribute.
An ID reference points at nothing, such as aria-controls or aria-activedescendant naming a missing id
Section titled “An ID reference points at nothing, such as aria-controls or aria-activedescendant naming a missing id”Point it at an element that exists. axe allows a missing aria-controls target while aria-expanded="false", for a popup rendered only when open. A missing aria-labelledby or aria-describedby target gets a review instead: check that the name or description it should supply is there.
A number has a unit or is out of range, such as aria-valuenow="50%" or aria-level="0"
Section titled “A number has a unit or is out of range, such as aria-valuenow="50%" or aria-level="0"”Write numbers as plain numbers, such as aria-valuenow="50" with aria-valuemin and aria-valuemax, and put a formatted value in aria-valuetext. Levels and positions start at 1.
aria-errormessage points at a hidden element, or at a message nothing announces
Section titled “aria-errormessage points at a hidden element, or at a message nothing announces”Show the message whenever aria-invalid="true", and also list its id in aria-describedby, or make it a live region, so screen readers announce it. Point at a single id.
<label for="email">Email</label><input id="email" type="email" aria-invalid="true" aria-errormessage="email-error" aria-describedby="email-error"><p id="email-error">Enter an email address such as name@example.com.</p>Fixes that fail
Section titled “Fixes that fail”Writing an unset script variable into the attribute, so it reads aria-expanded="undefined"
Section titled “Writing an unset script variable into the attribute, so it reads aria-expanded="undefined"”axe accepts it, since undefined is a value ARIA defines, but it means the element does not expand at all, so screen readers announce no state. Write "true" or "false".
Removing the ID reference to silence the rule
Section titled “Removing the ID reference to silence the rule”The rule passes, but the relationship is gone, such as which popup a combobox controls or which option is active.
In React
Section titled “In React”The part of the fix that differs in React, in its syntax. The rest of the guidance holds.
aria-invalid gets an error message or other value, such as aria-invalid={errors.email}
Section titled “aria-invalid gets an error message or other value, such as aria-invalid={errors.email}”Pass a boolean, aria-invalid={Boolean(error)}, which React writes as "true" or "false", and show the message in an element that aria-describedby points at.
import { useId } from 'react';
function EmailField({ error }) { const id = useId(); return ( <> <label htmlFor={id}>Email</label> <input id={id} type="email" aria-invalid={Boolean(error)} aria-describedby={error ? `${id}-error` : undefined} /> {error && <p id={`${id}-error`}>{error}</p>} </> );}A boolean is passed to an attribute that takes words, such as aria-live={true}
Section titled “A boolean is passed to an attribute that takes words, such as aria-live={true}”Pass one of the attribute’s own values, such as aria-live="polite". React writes a boolean as "true" or "false", which only some attributes accept.
aria-activedescendant keeps the id of an option that is no longer rendered
Section titled “aria-activedescendant keeps the id of an option that is no longer rendered”Derive it from state each render, and set it to undefined when no rendered option is active; React then leaves the attribute out.
More on the rule
Section titled “More on the rule”axe-core’s documentation of the rule
CORA’s fix guidance adapts content from accessibility-agents, under the MIT License: see its notice.