( Robust / WCAG 4.1.2 )

ARIA role is invalid

CriticalLevel AWCAG 4.1.2 — Name, Role, Value

What is this issue?

An element carries a role attribute whose value doesn’t match any role defined in the WAI-ARIA specification, for example role="buton" (a typo) or role="container" (not a real ARIA role, even though it sounds plausible). ARIA (Accessible Rich Internet Applications), a set of HTML attributes that describe the role, state, and properties of custom interface elements to assistive technology for cases semantic HTML can’t cover on its own, only works when its values come from a fixed, published vocabulary.

Abstract roles count as invalid too when used directly on an element. Roles like widget, structure, and range exist only to organize the ARIA specification’s role hierarchy into categories; they were never meant to be assigned to real markup, and using one produces the same failure as a misspelled role.

Why does this matter?

A role attribute is a promise to assistive technology about what kind of thing an element is. When that value isn’t a real role, the browser has nothing to map it to, so most assistive technology falls back to treating the element as if it had no role at all: a generic, unannounced chunk of content, or a bare “clickable” with no further identity.

The practical cost depends on what the element was supposed to be. An invalid role on a custom dropdown, tab, or slider strips away the one thing telling a screen reader user how to interact with it: whether to expect arrow keys, whether it’s a toggle, whether it has options to pick from. The visual design still looks like a working control; only assistive technology users discover, mid-task, that the thing they’re interacting with has no identifiable role, state, and property, the three things assistive technology needs to describe an element: its role (what kind of thing it is), its state (its current condition), and its properties (other characteristics).

Who is affected?

  • Screen reader users: hear a generic or unannounced element instead of the control’s real purpose (button, checkbox, tab), and lose any interaction expectations that role would normally set.
  • Voice control users: issue commands like “click button” or “click checkbox” that match against an element’s accessible role; an unrecognized role can leave the control invisible to those command patterns entirely.

What users experience

Renata uses JAWS on Windows to check out on an online furniture store. The “Add to cart” control is a styled <div> with role="buton", a typo introduced when a developer copy-pasted a component and mistyped the attribute. JAWS doesn’t recognize “buton” as a role, so it announces the element only as clickable text with no role at all: “Add to cart.” She can still activate it by pressing Enter after tabbing to it, but nothing told her in advance it was a button rather than a link, a checkbox, or plain text; she has to click blind and hope.

How do I fix it?

Correct the role value to match a real WAI-ARIA role, or use the matching native HTML element instead of a role at all. This works because assistive technology only recognizes roles from the published spec; the moment the value matches a real entry in that list, the browser exposes the correct role, state, and interaction model automatically.

Check the value against the WAI-ARIA roles specification if you’re unsure whether it’s real. In most cases where a <div> or <span> has been given a role to simulate a native control, replacing it with the actual HTML element (<button> for role="buton", <nav> for a “navigation” role) removes the need for a role attribute entirely, since the native element already carries the correct role, keyboard behavior, and focus handling built in.

Code Examples

Before
<div role="buton">Add to cart</div>
<div role="container">Featured products</div>
<nav role="navigation-menu">Main links</nav>
After
<!-- Best: use the native element, no role attribute needed -->
<button>Add to cart</button>

<!-- When a div genuinely needs a role, use a real one -->
<div role="region" aria-label="Featured products">...</div>

<!-- nav already carries a navigation role; drop the invalid override -->
<nav aria-label="Main links">...</nav>

The button case swaps the div-plus-role pattern for the native <button> element, which is both simpler and more reliable than any role could be, since keyboard activation and focus styling come for free. The “container” case shows the other path: keeping a <div> but pointing its role at a real value (region) instead of an invented one. The <nav> case removes the role entirely, since a <nav> element already exposes a navigation role natively and the invalid override was unnecessary from the start.

Common Mistakes

Mistake: “It looks like a reasonable word, so it’s probably a valid role.” ARIA roles come from a closed, published list, not from plain English. Words like container, wrapper, card, or panel sound like they should be roles because they describe common UI patterns, but none of them exist in the spec; the closest real equivalents are region, group, or article, and picking the wrong one still fails this check even if it “sounds right.”

Mistake: “An invalid role is harmless if the element isn’t interactive.” Even a non-interactive element with an invalid role loses its intended landmark or structural identity. A role="container" on a page section meant to be a landmark still fails to register as one, so screen reader users navigating by landmarks skip right past it as if it weren’t there.

Mistake: “Any role attribute is better than none.” A role attribute with an invalid value isn’t a weaker version of the correct role; it’s discarded entirely, and the element falls back to whatever its native HTML would have given it anyway. Adding role=“container” to a <div> gives it exactly the same accessibility as no role at all, so there’s no partial credit for trying.

How RedFlag Detects This

Automated: axe-core rule, runs on every scan. RedFlag calls axe-core’s aria-roles rule as part of every scan, restricted to the WCAG 2.0/2.1/2.2 A and AA rule set. The rule reads every element’s role attribute value (including each entry in a space-separated fallback list) and checks it against the published WAI-ARIA role vocabulary, flagging any value that isn’t a real, non-abstract role.

False negative: the rule only judges whether the role value exists in the spec, not whether it’s the right role for what the element does: a <div role="button"> used for something that behaves like a checkbox passes this specific check even though the role choice is still wrong for the interaction. False positive: none typical for this check, since matching a fixed vocabulary is a binary comparison axe-core evaluates reliably. Manual step: for every element with a role, confirm the role matches what the control actually does and how it actually behaves, not just that the spelling passes.

Manual Testing

  1. Open the page in Chrome or Firefox with NVDA or VoiceOver running.
  2. Tab to or navigate by element type to every custom control on the page (anything built from a <div> or <span> rather than a native interactive element).
  3. Listen to the announced role immediately after the accessible name; it should match what the control actually does (“button,” “checkbox,” “tab,” and so on).
  4. If the screen reader announces no role at all, or a role that doesn’t match the control’s real behavior, check the element’s role attribute value against the WAI-ARIA specification.
  5. Open the browser’s accessibility inspector (Chrome DevTools → Elements → Accessibility pane) and confirm the computed “Role” property is not blank or “generic” for a control that’s supposed to be interactive.

4.1.2 Name, Role, Value: Every interface component must expose a name, role, and current state to assistive technology, and the role must be one assistive technology can actually interpret. An invalid role fails this criterion at its most basic level: there’s no recognizable role being exposed at all.

ARIA attribute is not allowed for this role is the natural next check once a role is valid: it verifies the attributes paired with that role are ones the role actually supports.

Element uses a deprecated ARIA role covers a role value that’s technically real but has been phased out of the specification, a subtler variant of “wrong role” than an outright typo.

ARIA element is missing a required parent role and Element role is missing required child elements both assume a valid role as their starting point: they check the structural relationships around a role that’s already correctly spelled.

ARIA attribute name is not valid is the equivalent typo check for attribute names instead of role values: the same category of mistake, on aria-* attributes rather than role.

Focusable element or content has an accessible name depends on the role being correct first, since a control’s expected naming method is determined by its role.

References

Frequently asked questions

Does an invalid role attribute break the element visually?

No. The role attribute is invisible to the browser's rendering engine and has no effect on layout, color, or appearance. It only changes what assistive technology announces, so an invalid role can sit unnoticed in production for years because it never causes a visual bug.

What happens if I just remove an invalid role instead of fixing it?

The element falls back to its native HTML role. A div or span with no role becomes generic or has no role at all, and a semantic element like button or nav keeps its built-in role. Removing a bad custom role is often safer than guessing a replacement, especially if the native element already does the job.

Are abstract ARIA roles like widget or structure valid to use directly?

No. Abstract roles such as widget, structure, section, and range exist only to organize the ARIA specification itself into categories. They are not meant to be assigned to real elements, and axe-core treats an abstract role used in markup the same as any other invalid role value.

Can a role attribute have more than one value?

Yes. ARIA 1.2 allows a space-separated list of role values as a fallback chain, where assistive technology uses the first value it recognizes and ignores the rest. This check still fails if every value in the list is invalid, but a valid fallback after an unrecognized custom value passes.

Is role="none" the same as removing the role entirely?

Functionally, yes, for most elements. role="none" is a valid, intentional role that tells assistive technology to ignore the element's semantics entirely, similar to role="presentation". It is a real, spec-defined value, not an error, so it never triggers this check.