ARIA Rule

ARIA roles must be valid aria-roles

The role attribute only accepts names from the ARIA list: dialog, button, tab, listbox and so on. A made-up value like role="modal", a typo like role="buton", or an abstract role like widget that exists only to organise the specification, is dropped by the browser. The element then falls back to whatever it was underneath, which for a div is nothing.

Websight reports this as ARIA roles used must conform to valid values

Who it affects

Someone using a screen reader has a confirmation box open on top of the page and hears no dialog, no name, just a heading and two buttons in the middle of the content they were reading.

Someone using a screen reader's landmark or dialog shortcuts cannot jump to the dialog because the browser does not know it is one.

Someone using voice control cannot address the element by its role (say, show dialogs) because it has none.

Broken versus fixed

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

Broken

It is obviously a dialog. role="modal" is not a real role, so a screen reader announces only: Delete this file, heading level 2. No dialog, and the aria-labelledby is wasted because a div cannot be named.

Fixed

Announced: Delete this file?, dialog. Moving focus into the dialog and trapping it there is script you still need; the native dialog element does most of that for you.

Show the markup behind both examples
Broken
<style>body{font-family:system-ui;padding:16px;margin:0;background:#edf2f7}.dlg{width:280px;padding:16px;border-radius:10px;background:#fff;box-shadow:0 8px 24px rgba(0,0,0,.15)}.dlg h2{margin:0 0 8px;font-size:17px}.dlg p{margin:0 0 14px;color:#4a5568;font-size:14px}.dlg button{padding:6px 12px;border-radius:6px;border:1px solid #cbd5e0;background:#fff;margin-right:6px}.danger{background:#c53030;color:#fff;border-color:#c53030}</style>
<div class="dlg" role="modal" aria-labelledby="t">
  <h2 id="t">Delete this file?</h2>
  <p>invoice-march.pdf will be removed for good.</p>
  <button>Cancel</button><button class="danger">Delete</button>
</div>
Fixed
<style>body{font-family:system-ui;padding:16px;margin:0;background:#edf2f7}.dlg{width:280px;padding:16px;border-radius:10px;background:#fff;box-shadow:0 8px 24px rgba(0,0,0,.15)}.dlg h2{margin:0 0 8px;font-size:17px}.dlg p{margin:0 0 14px;color:#4a5568;font-size:14px}.dlg button{padding:6px 12px;border-radius:6px;border:1px solid #cbd5e0;background:#fff;margin-right:6px}.danger{background:#c53030;color:#fff;border-color:#c53030}</style>
<div class="dlg" role="dialog" aria-modal="true" aria-labelledby="t">
  <h2 id="t">Delete this file?</h2>
  <p>invoice-march.pdf will be removed for good.</p>
  <button>Cancel</button><button class="danger">Delete</button>
</div>

Test it by hand in two minutes

  1. Search the page source for role=" and read each value. Anything that is not a word you recognise from the ARIA role list (dialog, button, tab, listbox, navigation, img and so on) is suspect.
  2. In Chrome DevTools, select the element and open the Accessibility pane. The computed Role shows what the browser actually took from your attribute; generic means it was ignored.
  3. Turn on VoiceOver (Cmd+F5) or NVDA and move to the element. You should hear the role you intended.
  4. Check the value has no stray spaces or capital letters, and that if two roles are listed the first one is the one you want (only the first valid one counts).

The fix

Before
<div role="modal" aria-labelledby="t">...</div>
After
<div role="dialog" aria-modal="true" aria-labelledby="t">...</div>

Replace the value with the real role that means what you meant. If no real role fits, you probably do not need the attribute; and if a native element exists (dialog, button, nav), use that instead and drop the role.

Typo in a common role

Before
<div role="buton" tabindex="0">Save</div>
After
<button>Save</button>

A native button is shorter, already focusable, and works with Enter and Space without script.

Abstract role

Before
<div role="widget">...</div>
After
<div>...</div>

Abstract roles like widget, input, section and landmark exist to organise the specification. They must never appear in markup.

Invented role on navigation

Before
<div role="nav">...</div>
After
<nav>...</nav>

The ARIA role is navigation, not nav. The nav element already has it.

Common mistakes

  • Inventing roles from class names or component names (role="card", role="hero").
  • Using HTML element names as roles (role="nav", role="section", role="input").
  • Writing the role with capitals or a trailing space, which some browsers reject.
  • Fixing the role but leaving the element as a div, so the keyboard behaviour the role implies is still missing.
  • Removing the role attribute entirely from something that genuinely needs one, like a custom dialog.

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 compares every role value against the list of valid, non-abstract ARIA roles, so typos and inventions are caught reliably. It cannot tell whether the valid role you chose is the right one for the behaviour 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.