aria-valid-attr-value

aria-* attribute values must match their declared type

automaticWCAG 4.1.2 (A)confidence: highseverity: seriousrobust

Checks that every recognized aria-* attribute has a value conforming to its WAI-ARIA-declared value type (boolean, tristate, token, integer, number, or ID reference).

Applies to. Applies to any element carrying at least one recognized aria-* attribute (unrecognized attribute names are aria-valid-attr's concern, not evaluated here).

Expectation. Each attribute's value conforms to its WAI-ARIA-declared value type: boolean ("true"/"false"), tristate ("true"/"false"/"mixed"), a token from a fixed enumerated set, an integer (within the range WAI-ARIA sets for it), a real number, or an ID reference (list) that resolves to an existing element in the document. Boolean, tristate and token values are read in any case, as Chromium reads them (aria-checked="TRUE" is checked). Per ACT 6a7281's own applicability ("any state or property that is NOT empty"), an explicitly empty value, including a bare boolean-style attribute with no "=value" at all, e.g. aria-checked alone, is out of scope for every value type, not a violation: a common, deliberate pattern in templated markup (e.g. React conditionally rendering aria-describedby={hasError ? errorId : ''}). A value of the wrong type is a WAI-ARIA violation, which ACT 6a7281 says is "not required for conformance to WCAG 2.1 at any level". It is a FAIL only where it leaves a control's user-settable state or value, or the focused item, unstated, which SC 4.1.2 requires to be programmatically determinable: one of aria-checked, aria-pressed, aria-selected, aria-expanded or aria-valuenow on an element with a widget role, or any invalid aria-activedescendant. Anywhere else it is CANTTELL. Some attributes also constrain each other, checked once each is valid on its own:

  • On an element whose role is slider, scrollbar, progressbar, meter, separator or spinbutton: aria-valuemin greater than aria-valuemax fails (ARIA_VALUE_RANGE_INVERTED), and so does an aria-valuenow outside the range (ARIA_VALUENOW_OUT_OF_RANGE), since the browser then gives assistive technology the nearest end of the range instead (Chromium announces 10 for 50 of 0 to 10). The range is the stated one; else a native element's own (a <progress>, <meter>, range or number input, by its min and max), as HTML-AAM maps it; else ARIA's defaults of 0 and 100, except on a spinbutton, which has none. With aria-valuetext, which is announced in place of the number, the value is not compared.
  • aria-posinset greater than aria-setsize fails (ARIA_POSINSET_EXCEEDS_SETSIZE); a set size of -1 (unknown) is left alone.
  • aria-haspopup whose popup, an element aria-controls names (or one inside it), has another role: cantTell (ARIA_HASPOPUP_ROLE_MISMATCH), as a script may show another popup. true means menu. Compared only when a named element has a role.
  • aria-activedescendant naming an element that is neither inside the element nor inside one it names in aria-owns or aria-controls: cantTell (ARIA_ACTIVEDESCENDANT_OUTSIDE). A combobox is left out, as its options are in the popup it controls.

What a finding reports.

  • attr: the attribute whose value is not valid.
  • value: its value as written.
  • valueReason: what is wrong with it. On an ARIA_ATTR_VALUE_INVALID finding (either tier): expected-true-false, expected-true-false-undefined, expected-true-false-mixed, expected-integer, expected-number or invalid-token when it is not of the attribute's type, integer-out-of-range when it is below the lowest value ARIA allows, expected-single-idref when an attribute taking one id holds several, idref-not-found when aria-activedescendant's id matches no element. On a CANTTELL: idref-list-none-found when none of the ids in a list matches an element, idref-controls-not-found for the same on aria-controls.
  • For the constraints between attributes, the values compared: role, valuenow, valuemin, valuemax (the range used, defaults included); posinset, setsize; haspopup, expectedRole, popupRole; activedescendant.

These data.details fields help to read and reproduce a finding. Apart from reasonCode and the few fields Output schema names as stable, they are not a stable contract: a field may be renamed or dropped in a minor release, so don’t build on them.

Examples

pass
<div aria-hidden="true"></div>

"true" is a valid value for the boolean-typed aria-hidden.

fail
<div role="checkbox" tabindex="0" aria-checked="yes">Subscribe</div>

aria-checked is tristate; "yes" isn't a valid token, so the checkbox's checked state is not stated.

can't tell
<div aria-hidden="yes"></div>

aria-hidden is boolean-typed; "yes" isn't a valid boolean token. It carries no control's state, so whether a WCAG criterion fails depends on the page.

How to check

When this rule can’t decide, it reports cantTell and leaves the question to a person. Each question it can ask is listed here under its reason code, with the steps to answer it. A finding carries the same steps as howToCheck, in the scan’s language. See Output schema.

ARIA_ACTIVEDESCENDANT_OUTSIDE

  1. Inspect the element the report names and note the id in its aria-activedescendant and where that element sits.
  2. Focus the element and use the arrow keys to move the active item.
  3. With a screen reader running, listen whether each active item is announced as you move.
  4. Check whether the referenced element could be moved inside the element or tied to it with aria-owns or aria-controls.

Pass. Each active item is announced as the arrow keys move through it.

Fail. The screen reader announces nothing or the wrong item as the active one changes, so keyboard users cannot follow it, which fails WCAG 4.1.2.

ARIA_ATTR_VALUE_INVALID

  1. Inspect the element the report names and note the attribute, its value and the type of value WAI-ARIA expects for it.
  2. Work out what the attribute was meant to convey, such as a heading level, a live-region setting or a position in a set.
  3. Check whether the page shows that information and whether it is conveyed any other way that assistive technology reads.

Pass. The information is not shown on the page, or it is also conveyed another way. Still fix the value: WAI-ARIA gives it no meaning.

Fail. The page shows information, such as a heading level, that only this attribute was meant to convey, and its invalid value conveys nothing (WCAG 1.3.1).

ARIA_ATTR_VALUE_TARGET_ABSENT

  1. Inspect the element the report names and note the attribute and the ids it refers to.
  2. Use the page as a user would (open, expand, submit) to reach the state where the referenced element should exist.
  3. In the developer tools console, run document.getElementById('the-id') for each id and check for a near-miss id in the markup (different case, typo, or inside another shadow root).
  4. Open the Accessibility pane in the browser's developer tools and check whether the name, description or relation it should supply appears.
  5. If it does not appear, check in the Accessibility pane whether the element still gets that name or description another way, for example from its own text, a <label> or an aria-label.

Pass. The referenced element exists when it matters and the name, description or relation shows in the accessibility tree, or the element gets the same name or description another way.

Fail. The id never resolves (missing, misspelled, or across a shadow boundary) and nothing else gives the label, description or relation, so it is lost, which fails WCAG 4.1.2 (ACT 6a7281).

ARIA_HASPOPUP_ROLE_MISMATCH

  1. Inspect the element the report names and note its aria-haspopup value (menu, listbox, tree, grid, dialog; true means menu).
  2. Open the popup with the mouse and with the keyboard (Enter, Space or Down Arrow).
  3. Inspect the popup that appears and check its role in the Accessibility pane in the browser's developer tools.
  4. Compare the popup's role with the type aria-haspopup announces.

Pass. The popup that opens has the role aria-haspopup announces.

Fail. The popup that opens has another role than announced (such as a dialog announced as a menu), misleading screen reader users, which fails WCAG 4.1.2.

View source