ARIA Rule

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. Write yes instead of true, or point at an id that does not exist, and the browser ignores the attribute or treats it as something you did not intend. The attribute is spelt right, so the mistake is easy to miss in the source.

Websight reports this as ARIA attributes must conform to valid values

Who it affects

Someone using a screen reader lands on a folder in a tree view and is not told whether it is open or closed, so they do not know whether pressing Right will expand it or move into it.

Someone relying on a live region never hears the status message because aria-live was set to a word the browser does not recognise.

Someone using a screen reader on a form hears no label on a field because aria-labelledby points at an id that was renamed.

Broken versus fixed

Both examples are live and rendered in a sandboxed frame. The broken one is deliberately broken.

Broken

The arrows show Documents open and Pictures closed. A screen reader announces: Documents, tree item, 1 of 2, with no expanded or collapsed state, because yes and no are not values aria-expanded accepts.

Fixed

Announced: Documents, expanded, tree item, 1 of 2, and Pictures, collapsed, tree item, 2 of 2. Opening and closing still needs script; the demo is static.

Show the markup behind both examples
Broken
<style>body{font-family:system-ui;padding:16px;margin:0;font-size:14px}ul{list-style:none;margin:0;padding:0}li{padding:3px 0}ul ul{padding-left:22px}.f::before{content:"\25BE ";color:#718096}.c::before{content:"\25B8 ";color:#718096}.d::before{content:"\2022 ";color:#a0aec0}</style>
<ul role="tree" aria-label="Folders">
  <li role="treeitem" aria-expanded="yes" class="f" tabindex="0">Documents
    <ul role="group">
      <li role="treeitem" class="d">Invoices</li>
      <li role="treeitem" class="d">Receipts</li>
    </ul>
  </li>
  <li role="treeitem" aria-expanded="no" class="c">Pictures</li>
</ul>
Fixed
<style>body{font-family:system-ui;padding:16px;margin:0;font-size:14px}ul{list-style:none;margin:0;padding:0}li{padding:3px 0}ul ul{padding-left:22px}.f::before{content:"\25BE ";color:#718096}.c::before{content:"\25B8 ";color:#718096}.d::before{content:"\2022 ";color:#a0aec0}</style>
<ul role="tree" aria-label="Folders">
  <li role="treeitem" aria-expanded="true" class="f" tabindex="0">Documents
    <ul role="group">
      <li role="treeitem" class="d">Invoices</li>
      <li role="treeitem" class="d">Receipts</li>
    </ul>
  </li>
  <li role="treeitem" aria-expanded="false" class="c">Pictures</li>
</ul>

Test it by hand in two minutes

  1. Search the page source for aria- and read each value. True or false attributes (aria-expanded, aria-checked, aria-hidden, aria-pressed) must be exactly true or false, in lower case.
  2. For aria-labelledby, aria-describedby and aria-controls, copy each id in the value and search the page for id="that-id". Every one must exist, once.
  3. For list attributes (aria-live, aria-current, aria-haspopup, aria-sort), check the value is one of the words the attribute allows; polite not on, page not yes.
  4. Turn on VoiceOver (Cmd+F5) or NVDA and move to the element. The state or name the attribute was supposed to give should be spoken.

The fix

Before
<li role="treeitem" aria-expanded="yes">Documents</li>
After
<li role="treeitem" aria-expanded="true">Documents</li>

Use the exact value the attribute defines. When your script sets it, make sure it writes the string true or false, not a JavaScript boolean that ends up as something else, and not 1 or 0.

aria-labelledby pointing at a missing id

Before
<h2 id="billing-heading">Billing</h2>
<section aria-labelledby="billing">...</section>
After
<h2 id="billing-heading">Billing</h2>
<section aria-labelledby="billing-heading">...</section>

The value must be the id of an element that exists on the page. Renamed ids are the usual cause.

Live region with a made-up setting

Before
<div aria-live="on">3 items added to basket</div>
After
<div aria-live="polite">3 items added to basket</div>

aria-live accepts off, polite or assertive. Anything else is treated as off.

Common mistakes

  • Writing aria-expanded="expanded" or aria-checked="checked", mirroring the attribute name.
  • Setting aria-hidden="yes" or aria-hidden="1" and assuming the element is hidden.
  • Pointing aria-describedby at a class name instead of an id.
  • Leaving aria-controls referencing a panel that is only added to the page later by script.
  • Using aria-current="active"; the allowed values are page, step, location, date, time, true and false.

WCAG criteria this rule maps to

axe-core tags this rule as WCAG 2.2 level A. It is one of the checks for:

The scanner knows the value type of every ARIA attribute and checks each one, including whether referenced ids exist at scan time. It cannot tell whether a true should have been a false, so compare the state with what is on screen. Automated tools find only part of the accessibility problems on a page; a pass on this rule is not a pass on the criterion.

Scan your own page for this and 63 other checks

Open any page in Chrome, click Websight, and get a score out of 100 with every issue explained and a draft fix snippet per element.

Add Websight to Chrome

Free scans, no account, nothing leaves your computer. Automated tools find only part of the problems; the extension says so on every report.