Accessibility Guides for WCAG 2.0 (A)
Learn how to identify and fix common accessibility issues flagged by Axe Core — so your pages are inclusive and usable for everyone. Also check our HTML Validation Guides.
An image map is an <img> element linked to a <map> element via the usemap attribute. Inside the <map>, individual <area> elements define clickable hotspots that function as links. Each of these <area> elements is essentially a link, and like all links, it must have an accessible name that describes its purpose.
Why This Is an Accessibility Problem
Screen readers cannot interpret graphical content. When a user navigates to a clickable <area> that lacks alternative text, the screen reader has no meaningful label to announce. In many cases, it will fall back to reading the link URL or the image filename — neither of which conveys the purpose of the link. This critically impacts:
- Blind users and deafblind users who rely entirely on screen readers to navigate and interact with content.
- Users with mobility impairments who use assistive technologies like switch devices or voice control, which depend on accessible names to identify interactive elements.
This rule relates to the following WCAG success criteria:
-
WCAG 2.4.4 – Link Purpose (In Context) (Level A): The purpose of each link must be determinable from the link text alone, or from the link text combined with its programmatically determined context. An
<area>without alternative text has no discernible purpose. -
WCAG 4.1.2 – Name, Role, Value (Level A): All user interface components must have a programmatically determined name. An
<area>element is an interactive component, so it requires an accessible name.
This rule also applies under Section 508, EN 301 549, and Trusted Tester requirements.
How to Fix It
Ensure every active <area> element inside a <map> has an accessible name by using one of these methods:
-
altattribute (preferred) — Add descriptivealttext directly to the<area>element. -
aria-labelattribute — Provide a text label viaaria-label. -
aria-labelledbyattribute — Reference another element that contains the label text.
The alt text should clearly describe the purpose or destination of the link, not the visual appearance of the hotspot region.
Also, make sure the parent <img> element has its own alt attribute describing the image as a whole.
Examples
Incorrect: <area> Elements Without Alternative Text
In this example, the <area> elements have no alt text. Screen readers cannot communicate what each clickable region does.
<img src="solar_system.jpg" alt="Solar System" width="472" height="800" usemap="#solar-map">
<map name="solar-map">
<area shape="rect" coords="115,158,276,192" href="https://example.com/mercury">
<area shape="rect" coords="115,193,276,234" href="https://example.com/venus">
<area shape="rect" coords="115,235,276,280" href="https://example.com/earth">
</map>
Correct: <area> Elements With alt Text
Each <area> now has a descriptive alt attribute that communicates the link’s purpose.
<img src="solar_system.jpg" alt="Solar System" width="472" height="800" usemap="#solar-map">
<map name="solar-map">
<area shape="rect" coords="115,158,276,192" href="https://example.com/mercury" alt="Mercury">
<area shape="rect" coords="115,193,276,234" href="https://example.com/venus" alt="Venus">
<area shape="rect" coords="115,235,276,280" href="https://example.com/earth" alt="Earth">
</map>
Correct: Using aria-label Instead of alt
<img src="floor_plan.png" alt="Office floor plan" width="600" height="400" usemap="#office-map">
<map name="office-map">
<area shape="rect" coords="0,0,200,200" href="/rooms/conference-a" aria-label="Conference Room A">
<area shape="rect" coords="200,0,400,200" href="/rooms/kitchen" aria-label="Kitchen">
</map>
Incorrect: Server-Side Image Map
Server-side image maps use the ismap attribute and rely on mouse click coordinates sent to the server. These are not keyboard accessible and provide no text alternatives for individual regions. Avoid this pattern entirely.
<a href="/maps/nav.map">
<img src="navbar.gif" alt="Navigation" ismap>
</a>
Instead, replace server-side image maps with client-side image maps (usemap and <map>) that include proper alt text on each <area>, or use a different navigational pattern altogether such as a standard list of links.
The WAI-ARIA specification organizes roles, states, and properties into a strict taxonomy. Each role defines three categories of attributes it can use:
- Required attributes — must be present for the role to function correctly.
- Supported attributes — optionally enhance the role’s semantics.
- Inherited attributes — come from superclass roles in the ARIA role hierarchy.
Any ARIA attribute that doesn’t fall into one of these categories is not allowed on that role. This applies equally to explicit roles (set with the role attribute) and implicit roles that HTML elements carry by default. For instance, <button> has an implicit role of button, <input type="checkbox"> has an implicit role of checkbox, and <h2> has an implicit role of heading.
When an unsupported attribute appears on an element, the result is unpredictable. A screen reader might silently ignore it, or it might announce contradictory information — for example, describing a heading as a checkable control. In the worst case, invalid role-attribute combinations can break accessibility for entire sections of a page.
Who is affected
This issue has a critical impact on people who use assistive technologies:
- Screen reader users (blind and deafblind users) depend on accurate role and state information to understand and interact with content. Conflicting ARIA attributes can cause elements to be announced as something they are not.
- Voice control users rely on correctly exposed semantics to issue commands targeting specific controls. Misrepresented roles can make controls unreachable by voice.
- Users of switch devices and alternative input methods depend on tools that interpret ARIA roles and attributes to identify operable controls. Invalid attributes can make controls appear inoperable or misrepresent their purpose entirely.
When ARIA attributes conflict with an element’s role, these users may encounter controls that lie about what they do, states that never update correctly, or entire regions that become completely unusable.
Relevant WCAG success criteria
This rule relates to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), as well as EN 301 549 clause 9.4.1.2. This criterion requires that all user interface components expose their name, role, and value to assistive technologies in a way that can be programmatically determined. Using unsupported ARIA attributes on a role violates this criterion because it introduces properties that conflict with the element’s actual role, breaking the contract between the page and assistive technology.
How to fix the problem
-
Identify the element’s role. Check for an explicit
roleattribute. If none is present, determine the element’s implicit ARIA role from its HTML tag. For example,<input type="checkbox">has an implicit role ofcheckbox, and<nav>has an implicit role ofnavigation. -
Look up the allowed attributes for that role in the WAI-ARIA specification’s role definitions. Each role page lists its required states and properties, supported states and properties, and inherited properties from superclass roles.
-
Remove or relocate any ARIA attribute that isn’t in the allowed list. If the attribute belongs on a different element within your component, move it there.
-
Reconsider the role. Sometimes the right fix isn’t removing the attribute but changing the element’s role to one that supports the attribute you need. If you want a toggleable control, use
role="switch"orrole="checkbox"instead ofrole="button". -
Consult the ARIA in HTML specification for additional conformance rules about which ARIA attributes are appropriate on specific HTML elements, including restrictions on how elements can be named.
Examples
Incorrect: unsupported attribute on an explicit role
The aria-checked attribute is not supported on role="textbox" because a textbox is not a checkable control. A screen reader might announce this element as both a text input and a checked control.
<div role="textbox" aria-checked="true" contenteditable="true">
Enter your name
</div>
Correct: unsupported attribute removed
Remove aria-checked since it has no meaning on a textbox. Use aria-label to provide an accessible name.
<div role="textbox" contenteditable="true" aria-label="Your name">
</div>
Incorrect: unsupported attribute on an implicit role
The <h2> element has an implicit role of heading. The aria-selected attribute is not supported on headings because headings are not selectable items.
<h2 aria-selected="true">Account Settings</h2>
Correct: unsupported attribute removed from heading
If selection semantics aren’t needed, remove the attribute. If you need selection behavior, use an element with an appropriate role such as tab.
<h2>Account Settings</h2>
Incorrect: role doesn’t match the intended behavior
The developer wants a toggleable control but used role="button", which does not support aria-checked.
<div role="button" aria-checked="true" tabindex="0">
Dark mode
</div>
Correct: role changed to one that supports the attribute
Changing the role to switch makes aria-checked valid. The element remains keyboard-operable via tabindex="0".
<div role="switch" aria-checked="true" tabindex="0" aria-label="Dark mode">
Dark mode
</div>
Incorrect: unsupported attribute on a native HTML element
The <a> element has an implicit role of link. The aria-required attribute is not supported on links because links are not form fields that accept input.
<a href="/terms" aria-required="true">Terms of Service</a>
Correct: unsupported attribute removed from link
Remove aria-required from the link. If you need to indicate that agreeing to terms is mandatory, communicate that through a form control such as a checkbox.
<a href="/terms">Terms of Service</a>
Correct: supported attribute on a matching implicit role
The aria-expanded attribute is supported on the implicit button role, making this combination valid.
<button aria-expanded="false" aria-controls="menu-list">
Menu
</button>
<ul id="menu-list" hidden>
<li><a href="/home">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
The aria-braillelabel and aria-brailleroledescription attributes were introduced to give authors fine-grained control over how content is presented on refreshable braille displays. For example, a visual “4 stars” rating might be represented as **** in braille to save space and improve readability on a limited-cell display. Similarly, a custom role description like “slide” might be abbreviated to “sld” for braille output.
However, these braille-specific attributes are designed as overrides, not standalone values. They modify how an existing accessible name or role description is rendered in braille. If no accessible name or role description exists, there’s nothing for the braille attribute to override. The WAI-ARIA specification states that braille attributes without their non-braille equivalents should be ignored, but assistive technologies may not handle this consistently. Some screen readers might display the braille-only text while others ignore it entirely, leading to an unpredictable experience.
Who is affected
This issue primarily impacts users who are blind or deafblind and rely on refreshable braille displays. It can also affect users with mobility impairments who use assistive technologies that interpret ARIA attributes. When braille attributes lack their non-braille counterparts, these users may encounter missing labels or confusing role information, making it harder — or impossible — to understand and interact with content.
Related WCAG success criteria
This rule maps to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have an accessible name and role that can be programmatically determined. Using aria-braillelabel without a proper accessible name, or aria-brailleroledescription without aria-roledescription, means the element’s name or role description is not reliably communicated to assistive technologies. This applies to WCAG 2.0, 2.1, and 2.2 at Level A, as well as EN 301 549 guideline 9.4.1.2.
How to fix it
-
Add a non-braille equivalent. If an element has
aria-braillelabel, ensure it also has an accessible name — viaaria-label,aria-labelledby, visible text content, or thealtattribute on images. If an element hasaria-brailleroledescription, ensure it also hasaria-roledescription. -
Check attribute placement. The
aria-braillelabeloraria-brailleroledescriptionattribute might be on the wrong element. Verify it’s on the same element that has the corresponding accessible name or role description. - Remove unnecessary braille attributes. If the braille attribute isn’t serving a meaningful purpose (e.g., if the braille text would be the same as the accessible name), remove it entirely.
Examples
Incorrect: aria-braillelabel without an accessible name
The image has an empty alt attribute, so it has no accessible name. The aria-braillelabel has nothing to override.
<img alt="" aria-braillelabel="****" src="stars.jpg">
Correct: aria-braillelabel with an accessible name
The button has an accessible name from the image’s alt text. The aria-braillelabel overrides how that name appears on a braille display.
<button aria-braillelabel="****">
<img alt="4 stars" src="stars.jpg">
</button>
Incorrect: aria-brailleroledescription without aria-roledescription
The element has a braille role description but no aria-roledescription to serve as the non-braille equivalent.
<div
role="article"
aria-labelledby="slideheading"
aria-brailleroledescription="sld">
<h1 id="slideheading">My vacation in Rome</h1>
</div>
Correct: aria-brailleroledescription with aria-roledescription
Both aria-roledescription and aria-brailleroledescription are present, so the braille display can use the abbreviated version while screen readers use the full role description.
<div
role="article"
aria-labelledby="slideheading"
aria-roledescription="slide"
aria-brailleroledescription="sld">
<h1 id="slideheading">My vacation in Rome</h1>
</div>
Incorrect: aria-braillelabel on the wrong element
The aria-braillelabel is on a <span> that has no accessible name, even though the parent button does.
<button aria-label="Close">
<span aria-braillelabel="cls">X</span>
</button>
Correct: aria-braillelabel on the element with the accessible name
The aria-braillelabel is placed on the same element that has aria-label.
<button aria-label="Close" aria-braillelabel="cls">
<span>X</span>
</button>
When you assign a role like link, button, or menuitem to an element, you are telling the browser and assistive technologies that this element is an interactive command. Screen readers rely on the accessible name of these elements to communicate their purpose to the user. If no accessible name exists, a screen reader might announce something like “button” or “link” with no additional context — leaving the user with no way to understand what the control does.
This issue primarily affects users who are blind or have low vision and rely on screen readers, but it also impacts users with mobility impairments who may use voice control software to activate elements by name. If there is no name, voice control users cannot target the element with a spoken command.
Related WCAG Success Criteria
This rule maps to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A). This criterion requires that all user interface components have a name that can be programmatically determined. It applies across WCAG 2.0, 2.1, and 2.2, and is also referenced in EN 301 549 (9.4.1.2), Trusted Tester guidelines, and RGAA.
The Trusted Tester guidelines further specify that the purpose of each link or button must be determinable from some combination of its text, accessible name, accessible description, or programmatically determined context.
How to Fix It
Ensure that every element with role="link", role="button", or role="menuitem" has an accessible name through one of these methods:
- Inner text content — Place readable text inside the element.
-
aria-labelattribute — Add a non-emptyaria-labelwith a descriptive name. -
aria-labelledbyattribute — Point to theidof another element that contains visible, non-empty text. -
titleattribute — Use atitleattribute as a fallback (thougharia-labelor visible text is preferred).
When possible, prefer using native HTML elements (<a>, <button>) over custom ARIA roles, as they come with built-in accessibility behaviors. If you must use ARIA roles, always make sure the accessible name is clear and describes the action or destination.
Examples
Incorrect: No accessible name
These elements will be flagged because screen readers cannot determine their purpose.
<!-- Empty element with no text or label -->
<div role="link"></div>
<!-- Empty aria-label provides no name -->
<div role="button" aria-label=""></div>
<!-- aria-labelledby points to a non-existent element -->
<div role="menuitem" aria-labelledby="nonexistent"></div>
<!-- aria-labelledby points to an empty element -->
<div role="link" aria-labelledby="empty-label"></div>
<div id="empty-label"></div>
Correct: Accessible name provided
Each of these elements has a discernible name that screen readers can announce.
<!-- Inner text content -->
<div role="link" tabindex="0">Visit our help center</div>
<!-- aria-label attribute -->
<div role="button" tabindex="0" aria-label="Close dialog"></div>
<!-- aria-labelledby pointing to visible text -->
<div role="menuitem" tabindex="0" aria-labelledby="menu-label">
<span id="menu-label">Save document</span>
</div>
<!-- Combination of aria-label and inner text -->
<div role="link" tabindex="0" aria-label="Learn more about pricing">
Learn more
</div>
<!-- title attribute as a fallback -->
<div role="button" tabindex="0" title="Submit form"></div>
Preferred: Use native HTML elements
Native elements handle naming and keyboard behavior automatically, reducing the chance of accessibility issues.
<a href="/help">Visit our help center</a>
<button type="button">Close dialog</button>
Note: When testing with RGAA, issues reported by this rule may need to be mapped to a different RGAA test, such as 6.2.1 for links.
When ARIA attributes are applied to elements where the specification says they should not be used, the result is unpredictable behavior across browsers and assistive technologies. Different browsers handle these conflicts inconsistently — some ignore the ARIA attribute, others override the native state, and still others pass both values through. This inconsistency means that a screen reader user on one browser may hear a completely different state than a screen reader user on another, creating a confusing and unreliable experience.
This rule relates to WCAG 2.0/2.1/2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that for all user interface components, the name, role, and states can be programmatically determined. When ARIA attributes conflict with native semantics or are used outside their allowed context, the programmatically determined state becomes ambiguous or incorrect. Users affected include people who are blind or deafblind and rely on screen readers, as well as people with mobility disabilities who use alternative input devices that depend on accurate ARIA information.
There are two main scenarios this rule checks:
The aria-checked Attribute on Native Checkboxes
The aria-checked attribute should not be used on an <input type="checkbox"> element. Native HTML checkboxes already communicate their checked state to the browser’s accessibility tree through the checked property. When you add aria-checked on top of this, you create two competing sources of truth. If the native checked state and the aria-checked value fall out of sync — which is easy to do — some assistive technologies will report the native state while others report the ARIA state. The user has no way to know which is correct.
How to fix it
You have two options:
-
Remove
aria-checkedand rely on the nativecheckedattribute or property. If you need a “mixed” or indeterminate state, set theindeterminateproperty on the checkbox via JavaScript. -
Replace the native checkbox with a custom element (e.g., a
<div>or<span>) that usesrole="checkbox"along witharia-checked. When doing this, you must also ensure the element is keyboard accessible (focusable and togglable with Space) and has an accessible name.
Row-Specific Attributes Outside a treegrid
The attributes aria-posinset, aria-setsize, aria-expanded, and aria-level are only valid on a row (a <tr> element or an element with role="row") when that row is inside a treegrid. These attributes describe hierarchical tree relationships — position within a set, nesting level, and expandability — which are concepts that only make sense in a tree grid context. When used inside a plain <table> or grid, these attributes serve no function and may cause screen readers to announce confusing or nonsensical information.
How to fix it
Either remove the unsupported attributes from the rows, or change the parent container’s role to treegrid if the data truly represents a hierarchical, expandable structure. If you switch to treegrid, make sure cells use role="gridcell" and that the keyboard interaction pattern matches what users expect from a tree grid (arrow key navigation for expanding/collapsing rows).
Examples
Bad example: aria-checked on a native checkbox
The aria-checked attribute conflicts with the native checkbox state.
<label>
<input type="checkbox" aria-checked="true">
I agree to make my website accessible
</label>
Good example: native checkbox without aria-checked
The browser communicates the checked state natively — no ARIA override needed.
<label>
<input type="checkbox" checked>
I agree to make my website accessible
</label>
Good example: custom checkbox using aria-checked
When building a custom checkbox, aria-checked is appropriate because there is no native checked state.
<div role="checkbox" aria-checked="true" tabindex="0" aria-label="I agree to make my website accessible">
✓ I agree to make my website accessible
</div>
Bad example: tree attributes on rows inside a plain table
The aria-level and aria-expanded attributes are not valid on rows inside a <table>.
<table>
<tr aria-level="1" aria-expanded="false">
<td>My Downloads</td>
</tr>
<tr aria-level="2">
<td>Documents</td>
</tr>
</table>
Good example: tree attributes on rows inside a treegrid
Changing the table’s role to treegrid makes these attributes valid and meaningful.
<table role="treegrid">
<tr aria-level="1" aria-expanded="false">
<td role="gridcell">My Downloads</td>
</tr>
<tr aria-level="2" class="hidden">
<td role="gridcell">Documents</td>
</tr>
</table>
Good example: removing unsupported attributes from a plain table
If the data is not hierarchical, simply remove the tree-related attributes.
<table>
<tr>
<td>My Downloads</td>
</tr>
<tr>
<td>Documents</td>
</tr>
</table>
The WAI-ARIA specification evolves over time. As it matures, certain roles are deprecated because they were found to be redundant, poorly supported, or better served by other mechanisms. When you use a deprecated role, you’re relying on semantics that assistive technologies are no longer expected to support. This means the role may be completely ignored, misinterpreted, or cause unexpected behavior.
Users most affected include:
- Blind and deafblind users who rely on screen readers to convey the purpose and structure of elements on a page.
- Users with mobility impairments who depend on assistive technologies that use ARIA roles for navigation and interaction.
When a screen reader encounters an element with a deprecated role, it may announce it generically (e.g., as a simple group or section) or skip the role announcement entirely. This strips away meaningful context that helps users understand the content structure and interact with it effectively.
Relevant WCAG Success Criteria
This rule relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A). This criterion requires that for all user interface components, the name, role, and value can be programmatically determined and that changes to these are available to assistive technologies. Using a deprecated role means the role cannot be reliably determined, which violates this requirement.
This applies across WCAG 2.0, 2.1, and 2.2 at Level A, as well as EN 301 549 (guideline 9.4.1.2).
How to Fix It
-
Identify deprecated roles in your codebase. The axe-core rule checks all elements with a
roleattribute against the current WAI-ARIA specification. -
Replace deprecated roles with supported alternatives. Currently, the primary deprecated role flagged is:
-
directory: Replace withlist,tree, orsectiondepending on the content structure. If the directory represents a flat list of items (like a table of contents), uselist. If it represents a hierarchical, expandable structure, usetree. If it’s a general grouping of related content, usesection.
-
- Test with assistive technologies after making changes to confirm the new role conveys the intended meaning.
As the WAI-ARIA specification continues to evolve, additional roles may be deprecated in future versions. Keep your ARIA usage up to date by referencing the latest WAI-ARIA specification.
Examples
Incorrect: Using the Deprecated directory Role
<div role="directory">
<ul>
<li><a href="#section-1">Section 1</a></li>
<li><a href="#section-2">Section 2</a></li>
<li><a href="#section-3">Section 3</a></li>
</ul>
</div>
The directory role is deprecated. Assistive technologies may not recognize it, leaving users without useful semantic information about this element.
Correct: Using a Supported Role Instead
If the content is a simple list of links (such as a table of contents), use list or rely on the native <ul> semantics:
<nav aria-label="Table of contents">
<ul>
<li><a href="#section-1">Section 1</a></li>
<li><a href="#section-2">Section 2</a></li>
<li><a href="#section-3">Section 3</a></li>
</ul>
</nav>
In this example, the native <ul> element already provides the list role, and wrapping it in a <nav> with an aria-label gives assistive technology users clear context about its purpose.
Correct: Using tree for Hierarchical Content
If the content represents an expandable, nested structure, use the tree role with appropriate child roles:
<ul role="tree" aria-label="Site map">
<li role="treeitem" aria-expanded="true">
Section 1
<ul role="group">
<li role="treeitem">Subsection 1.1</li>
<li role="treeitem">Subsection 1.2</li>
</ul>
</li>
<li role="treeitem">Section 2</li>
</ul>
Correct: Using section for a General Content Grouping
If the deprecated directory role was used simply to group related content, a <section> element with an accessible name is a good replacement:
<section aria-label="Staff directory">
<ul>
<li>Alex Johnson — Engineering</li>
<li>Maria Garcia — Design</li>
<li>Sam Lee — Marketing</li>
</ul>
</section>
The key is to choose a replacement that accurately reflects the structure and purpose of the content, ensuring assistive technologies can convey it meaningfully to users.
The aria-hidden attribute controls whether an element and its children are exposed to the accessibility tree — the data structure that assistive technologies use to interpret and present web content. When set to "true", the element and all of its descendants are hidden from screen readers and other assistive tools. Applying this attribute to the <body> element is catastrophic because the <body> contains all visible page content, meaning nothing on the page will be announced or navigable for assistive technology users.
This issue has a critical impact on users who are blind or have low vision and rely on screen readers. While sighted users can see and interact with the page normally, screen reader users experience a completely empty page. Keyboard users may still be able to tab to focusable elements like links and buttons, but the screen reader will remain silent — providing no context about what those elements are or what the page contains.
This rule relates to two WCAG 2.0/2.1/2.2 Level A success criteria:
- WCAG 1.3.1 (Info and Relationships): Content structure and relationships must be programmatically determinable. Hiding the entire body breaks this requirement because no structural information reaches assistive technologies.
-
WCAG 4.1.2 (Name, Role, Value): All user interface components must have accessible names and roles. When
aria-hidden="true"is on the<body>, no component can communicate its name, role, or value to assistive technologies.
How to Fix It
The fix is straightforward: remove the aria-hidden="true" attribute from the <body> element.
If aria-hidden="true" was added to the <body> intentionally — for example, as part of a modal dialog pattern — restructure your approach. Instead of hiding the entire body, hide only the background content behind the modal using aria-hidden="true" on sibling wrapper elements, or use the <dialog> element with the inert attribute on background content.
Here are some important things to keep in mind:
-
Never apply
aria-hidden="true"to the<body>element. There is no valid use case for this. -
Use
aria-hidden="true"sparingly and only on non-interactive, decorative, or redundant content that provides no value to screen reader users. -
Be cautious with
aria-hidden="false". It does not reliably re-expose content that is already hidden via CSS (display: none,visibility: hidden) or the HTMLhiddenattribute. Always test thoroughly if you rely on this approach.
Examples
Incorrect: aria-hidden="true" on the body
This makes the entire page inaccessible to assistive technologies:
<body aria-hidden="true">
<header>
<h1>Welcome to My Site</h1>
<nav>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<main>
<p>This content is invisible to screen readers.</p>
</main>
</body>
Correct: No aria-hidden on the body
Simply remove the attribute from the <body> element:
<body>
<header>
<h1>Welcome to My Site</h1>
<nav>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<main>
<p>This content is now accessible to everyone.</p>
</main>
</body>
Correct: Hiding background content behind a modal dialog
If your original intent was to hide background content while a modal is open, hide the background wrapper — not the <body>:
<body>
<div id="page-wrapper" aria-hidden="true">
<header>
<h1>Welcome to My Site</h1>
</header>
<main>
<p>Main page content hidden while modal is open.</p>
</main>
</div>
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Action</h2>
<p>Are you sure you want to proceed?</p>
<button>Yes</button>
<button>Cancel</button>
</div>
</body>
In this pattern, only the #page-wrapper is hidden from assistive technologies, while the dialog remains fully accessible. Remember to remove aria-hidden="true" from the wrapper when the dialog is closed.
The aria-hidden="true" attribute tells assistive technologies to ignore an element and all of its descendants. This is useful for hiding purely decorative content — such as icon fonts or redundant visuals — that would clutter the screen reader experience. However, a serious problem arises when focusable elements like links, buttons, form inputs, or elements with tabindex="0" exist inside an aria-hidden="true" container.
When this happens, keyboard users can still Tab to those elements, but screen readers won’t announce them. The user lands on what feels like an invisible, unlabeled control. They have no way to know what the element is or what it does. This affects blind users, deafblind users, users with low vision who rely on screen readers, and mobility-impaired users who navigate exclusively by keyboard.
It’s also important to understand that aria-hidden="false" on a descendant does not override aria-hidden="true" on an ancestor. Once a parent is hidden from the accessibility tree, all children remain hidden regardless of their own aria-hidden value. Any focusable children inside that subtree still create the same problem.
Related WCAG Success Criteria
This rule primarily relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A). This criterion requires that for all user interface components, the name and role can be programmatically determined, and states, properties, and values can be programmatically set. A focusable element inside an aria-hidden="true" container violates this because its name and role are stripped from the accessibility API while it remains reachable via keyboard — making it impossible for assistive technologies to convey its purpose. This rule is flagged across WCAG 2.0, 2.1, and 2.2 at Level A, as well as in Trusted Tester, EN 301 549, and RGAA guidelines.
How to Fix It
There are several strategies to resolve this issue:
-
Remove
aria-hidden="true"from elements that contain focusable children, if those children need to be interactive. -
Remove focusable elements from inside the
aria-hidden="true"container if the entire section is truly meant to be hidden. -
Make focusable elements unfocusable by adding
tabindex="-1"to links, buttons, or other interactive elements inside the hidden container. -
Use the
disabledattribute on form controls (notaria-disabled, which does not actually prevent focus). -
Hide elements with CSS using
display: noneorvisibility: hidden, which removes them from both the accessibility tree and the focus order simultaneously.
If you need to hide content from assistive technologies, ensure equivalent meaning and functionality is still available through other accessible means.
Examples
Incorrect: Focusable link inside aria-hidden="true"
The link is removed from the accessibility tree but still receives keyboard focus.
<div aria-hidden="true">
<a href="/home">Home</a>
</div>
Incorrect: Offscreen focusable link inside aria-hidden="true"
Moving a link offscreen does not remove it from the focus order.
<div aria-hidden="true">
<a href="/" style="position:absolute; top:-999em">Link</a>
</div>
Incorrect: Using aria-disabled instead of disabled
The aria-disabled attribute does not actually prevent the input from receiving focus.
<div aria-hidden="true">
<input aria-disabled="true" />
</div>
Incorrect: Element with tabindex="0" and aria-hidden="true"
Adding tabindex="0" makes a normally non-focusable element focusable, creating a conflict with aria-hidden="true".
<p tabindex="0" aria-hidden="true">Some descriptive text</p>
Incorrect: Trying to override aria-hidden on a descendant
Setting aria-hidden="false" on a child does not re-expose it when a parent has aria-hidden="true". The button remains hidden from assistive technologies but still receives focus.
<div aria-hidden="true">
<div aria-hidden="false">
<button>Submit</button>
</div>
</div>
Incorrect: Focusable <summary> inside aria-hidden="true"
The <summary> element is natively focusable.
<details aria-hidden="true">
<summary>More info</summary>
<p>Additional details here.</p>
</details>
Correct: Non-focusable content inside aria-hidden="true"
A paragraph with no interactive elements is safe to hide.
<p aria-hidden="true">Decorative description text</p>
Correct: Focusable elements hidden with CSS display: none
Using display: none removes the link from both the focus order and the accessibility tree.
<div aria-hidden="true">
<a href="/" style="display:none">Link</a>
</div>
Correct: Focusable elements made unfocusable with tabindex="-1"
Adding tabindex="-1" removes the button from the tab order.
<div aria-hidden="true">
<button tabindex="-1">Close</button>
</div>
Correct: Form input properly disabled with the disabled attribute
The disabled attribute prevents the input from receiving focus entirely.
<input disabled aria-hidden="true" />
Correct: Removing aria-hidden and keeping elements interactive
If the content needs to be focusable, simply don’t hide it from assistive technologies.
<div>
<a href="/home">Home</a>
</div>
When you use ARIA roles to create custom input controls instead of native HTML elements, the browser no longer automatically associates a label with the control. Native <input>, <select>, and <textarea> elements support the <label> element through implicit wrapping or explicit for/id pairing, but custom <div> or <span> elements with ARIA roles do not. This means you must supply an accessible name through ARIA-specific mechanisms.
This rule applies to elements with any of the following ARIA input roles:
-
combobox -
listbox -
searchbox -
slider -
spinbutton -
textbox
Who is affected
Without an accessible name, users who rely on screen readers (including blind and deafblind users) hear only the role of the element — for example, “textbox” — with no indication of what information to enter. Users with mobility impairments who navigate via voice control also depend on accessible names to target specific controls by speaking their labels. This creates a serious barrier to interaction.
Related WCAG success criteria
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A). This criterion requires that all user interface components have a name that can be programmatically determined by assistive technologies. It is a Level A requirement, meaning it represents the minimum baseline of accessibility.
How to fix the problem
Because <label> elements do not work reliably with non-native elements like <div> or <span>, you need to use one of these approaches:
-
aria-label— Add a concise, descriptive label directly on the element. -
aria-labelledby— Point to theidof one or more visible elements that serve as the label. -
title— Use thetitleattribute as a last resort (it is less discoverable for sighted users).
Make sure the accessible name you provide is not empty or whitespace-only, and that any id referenced by aria-labelledby actually exists in the DOM.
Common mistakes
-
Setting
aria-labelto an empty or whitespace-only string (e.g.,aria-label=" "). -
Using
aria-labelledbywith anidthat doesn’t exist on the page. -
Wrapping a custom ARIA widget in a
<label>element, which does not provide a programmatic name for non-native elements in most browser/screen reader combinations. -
Using
<label for="id">to point to a<div>— theforattribute only works with native labelable elements like<input>,<select>, and<textarea>.
Examples
Incorrect: no accessible name on a combobox
The <div> has a combobox role but no label of any kind.
<div role="combobox">England</div>
Incorrect: aria-label is only whitespace
An empty or whitespace-only aria-label does not count as an accessible name.
<div aria-label=" " role="combobox">England</div>
Incorrect: aria-labelledby references a non-existent id
The referenced id must exist in the document; otherwise the accessible name resolves to empty.
<div role="listbox" aria-labelledby="color-label">
<div role="option">Orange</div>
</div>
<!-- No element with id="color-label" exists -->
Incorrect: <label> wrapping a non-native element
Implicit label association does not work for <div> elements with ARIA roles.
<label>
First name
<div role="textbox"></div>
</label>
Incorrect: explicit <label for> targeting a non-native element
The for attribute only creates a programmatic association with native labelable elements.
<label for="name-field">First name</label>
<div role="textbox" id="name-field"></div>
Correct: aria-label on a combobox
<div role="combobox" aria-label="Country">England</div>
Correct: aria-labelledby pointing to a visible label
<p id="color-label">Select a color:</p>
<div role="listbox" aria-labelledby="color-label">
<div role="option">Orange</div>
</div>
Correct: aria-labelledby on a searchbox
<p id="search-label">Search currency pairs:</p>
<div role="searchbox"
contenteditable="true"
aria-labelledby="search-label"></div>
Correct: aria-label on a slider
<div role="slider"
aria-label="Choose a value"
aria-valuemin="1"
aria-valuemax="7"
aria-valuenow="2"
tabindex="0"></div>
Correct: aria-label on a spinbutton
<div role="spinbutton"
aria-label="Enter quantity"
aria-valuemin="0"
aria-valuemax="10"
aria-valuenow="8"
tabindex="0"></div>
Correct: aria-labelledby on a textbox
<p id="name-label">First name</p>
<div role="textbox"
contenteditable="true"
aria-labelledby="name-label"></div>
When possible, prefer native HTML form controls (<input>, <select>, <textarea>) because they have built-in label support and keyboard behavior. Use custom ARIA input roles only when native elements cannot meet your design requirements, and always ensure those custom controls have an accessible name.
The meter role in ARIA represents a scalar measurement within a known range — think of a gauge showing a value like CPU temperature, password strength, or storage capacity. When a screen reader encounters an element with role="meter", it announces the element as a meter, but without an accessible name, it cannot convey what is being measured. The user hears something like “meter” with no context, which is effectively meaningless.
This issue primarily affects users who are blind or have low vision and rely on screen readers, as well as users with mobility impairments who may navigate via assistive technologies. It relates to WCAG 2.0/2.1/2.2 Success Criterion 1.1.1: Non-text Content (Level A), which requires that all non-text content has a text alternative that serves an equivalent purpose. A meter without a name fails to provide this text alternative.
How to Fix
Ensure every element with role="meter" has a clear, descriptive accessible name using one of these methods:
-
aria-label— Add descriptive text directly to the element. -
aria-labelledby— Reference another visible element that contains the label text. The referenced element must exist and contain non-empty text. -
title— Use thetitleattribute as a fallback naming method (thougharia-labeloraria-labelledbyare generally preferred).
The name should clearly describe what the meter is measuring so users understand its purpose in context.
Examples
Incorrect: Meter with no accessible name
The following meter has no name at all. A screen reader will announce it as a meter but cannot tell the user what it measures.
<div role="meter" aria-valuemin="0" aria-valuemax="100" aria-valuenow="75"></div>
Incorrect: Empty aria-label
An empty aria-label is equivalent to having no name.
<div role="meter" aria-label="" aria-valuemin="0" aria-valuemax="100" aria-valuenow="75"></div>
Incorrect: aria-labelledby referencing a nonexistent or empty element
If the referenced element doesn’t exist or has no text content, the meter still lacks an accessible name.
<div role="meter" aria-labelledby="nonexistent" aria-valuemin="0" aria-valuemax="100" aria-valuenow="75"></div>
<div role="meter" aria-labelledby="empty-label" aria-valuemin="0" aria-valuemax="100" aria-valuenow="75"></div>
<div id="empty-label"></div>
Correct: Using aria-label
<div role="meter" aria-label="Disk usage" aria-valuemin="0" aria-valuemax="100" aria-valuenow="75"></div>
Correct: Using aria-labelledby
<span id="meter-label">Battery level</span>
<div role="meter" aria-labelledby="meter-label" aria-valuemin="0" aria-valuemax="100" aria-valuenow="42"></div>
Correct: Using the title attribute
<div role="meter" title="Signal strength" aria-valuemin="0" aria-valuemax="5" aria-valuenow="3"></div>
Correct: Using the native <meter> element with a <label>
When possible, prefer the native HTML <meter> element, which has built-in semantics and can be associated with a <label>.
<label for="fuel">Fuel level</label>
<meter id="fuel" min="0" max="100" value="68">68%</meter>
When a progress bar lacks an accessible name, users who rely on assistive technologies have no way to distinguish it from other progress bars on the page or understand its purpose. Imagine a page with two progress bars—one for a file upload and one for a software installation. Without accessible names, a screen reader user would hear “progress bar” twice with no context for either. This issue primarily affects users who are blind or have low vision, as well as users with mobility impairments who may navigate using voice commands that reference element names.
This rule relates to WCAG Success Criterion 1.1.1: Non-text Content (Level A), which requires that all non-text content has a text alternative that serves the equivalent purpose. A progress bar is a non-text indicator of status, so it needs a text-based name to convey its meaning to assistive technology users. This criterion applies across WCAG 2.0, 2.1, and 2.2 at Level A, meaning it is a baseline requirement.
How to Fix
Ensure every element with role="progressbar" has a meaningful accessible name using one of these techniques:
-
aria-label— Provide a concise, descriptive label directly on the element. -
aria-labelledby— Reference theidof another element that contains visible label text. -
title— Use thetitleattribute as a fallback (thougharia-labeloraria-labelledbyare preferred).
The name should clearly describe what process or task the progress bar represents, such as “File upload progress” or “Installation progress.”
Examples
Failing: Progress bar with no accessible name
A progress bar with no labeling attributes is announced generically with no context.
<div role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
</div>
Failing: Empty aria-label
An empty aria-label provides no name, so the progress bar remains unlabeled.
<div role="progressbar" aria-label="" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">
</div>
Failing: aria-labelledby pointing to a nonexistent or empty element
If the referenced element doesn’t exist or has no text content, the progress bar still has no accessible name.
<div role="progressbar" aria-labelledby="missing-id" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100">
</div>
<!-- Or referencing an empty element -->
<div role="progressbar" aria-labelledby="empty-label" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100">
</div>
<div id="empty-label"></div>
Passing: Using aria-label
<div role="progressbar" aria-label="File upload progress" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
</div>
A screen reader will announce something like: “File upload progress, progress bar, 50%.”
Passing: Using aria-labelledby
<h3 id="upload-heading">Uploading resume.pdf</h3>
<div role="progressbar" aria-labelledby="upload-heading" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100">
</div>
This approach is especially useful when a visible heading or label already describes the progress bar, keeping the visual and accessible names in sync.
Passing: Using title
<div role="progressbar" title="Installation progress" aria-valuenow="90" aria-valuemin="0" aria-valuemax="100">
</div>
The title attribute works as a naming mechanism, but note that it may also produce a tooltip on hover. Prefer aria-label or aria-labelledby when possible for a more consistent experience across assistive technologies.
The WAI-ARIA specification defines which attributes are permitted, required, or prohibited for each role. When you use a prohibited attribute on a role, you’re relying on a combination that assistive technologies are not designed to support. The result is that the information you intended to communicate — such as a label or description — may be silently dropped.
This creates a serious problem for users who depend on assistive technologies, including people who are blind, deafblind, or have mobility impairments. Screen readers might ignore the prohibited attribute, leaving the user without context. Some assistive technologies may attempt to compensate, leading to inconsistent or confusing behavior across different tools and browsers.
A common example is using aria-label on an element with role="presentation" or role="none". These roles explicitly tell assistive technologies to ignore the element’s semantics, so labeling it with aria-label is contradictory. Similarly, text-level semantic roles like code, insertion, deletion, strong, emphasis, subscript, superscript, and paragraph prohibit aria-label and aria-labelledby because these roles represent inline text content that should not carry an accessible name separate from their text content.
Related WCAG success criteria
This rule relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that for all user interface components, the name, role, and states/properties can be programmatically determined. Using prohibited ARIA attributes violates this criterion because the intended name or state information cannot be reliably communicated to assistive technologies.
How to fix it
When axe flags a prohibited ARIA attribute, consider these approaches:
- Remove the prohibited attribute — If the information it conveys is not essential, simply remove it.
-
Change the element’s role — Switch to a role that permits the attribute you need. For instance, if you need
aria-label, don’t userole="none". - Provide the information as visible text — Instead of relying on the prohibited attribute, include the information directly in the page content where all users can access it.
- Move the attribute to a different element — Place the attribute on a parent or child element that has a role supporting it.
Examples
Incorrect: aria-label on an element with role="presentation"
The presentation role tells assistive technologies to ignore the element. Adding aria-label contradicts this and will be ignored.
<div role="presentation" aria-label="Navigation section">
<a href="/home">Home</a>
<a href="/about">About</a>
</div>
Correct: Use a role that supports aria-label
If the element needs an accessible name, use a role that permits labeling, such as navigation.
<nav aria-label="Navigation section">
<a href="/home">Home</a>
<a href="/about">About</a>
</nav>
Incorrect: aria-label on a text-level semantic role
Roles like code, strong, emphasis, insertion, and deletion prohibit aria-label and aria-labelledby.
<span role="code" aria-label="JavaScript variable declaration">
const x = 10;
</span>
Correct: Use visible text instead
Provide context through surrounding text content rather than a prohibited attribute.
<p>
The following JavaScript variable declaration
<code>const x = 10;</code>
assigns the value 10 to x.
</p>
Incorrect: aria-labelledby on role="none"
<h2 id="section-title">Features</h2>
<table role="none" aria-labelledby="section-title">
<tr>
<td>Fast</td>
<td>Reliable</td>
</tr>
</table>
Correct: Remove the conflicting role or the prohibited attribute
If the table needs to be labeled, remove role="none" so it retains its native table semantics.
<h2 id="section-title">Features</h2>
<table aria-labelledby="section-title">
<tr>
<td>Fast</td>
<td>Reliable</td>
</tr>
</table>
If the table is truly presentational and doesn’t need a label, remove the aria-labelledby attribute instead.
<table role="none">
<tr>
<td>Fast</td>
<td>Reliable</td>
</tr>
</table>
Why This Is an Accessibility Problem
ARIA roles describe what an element is and how it behaves. Many roles depend on specific state or property attributes to convey critical information about the element. For example, a checkbox role requires aria-checked so users know whether the checkbox is selected. Without it, a screen reader user hears “checkbox” but has no idea whether it’s checked or unchecked.
This issue affects users who are blind, deafblind, or have mobility impairments and rely on assistive technologies to interact with web content. When required attributes are missing, these users lose essential context about the state of interactive widgets.
This rule relates to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that for all user interface components, the name, role, and states can be programmatically determined. Missing required ARIA attributes directly violate this criterion because the element’s state cannot be communicated to assistive technologies.
How Required Attributes Work
Each ARIA role has a set of required states and properties defined in the WAI-ARIA specification. These attributes are essential for the role to function correctly. Some common examples:
| Role | Required Attribute(s) |
|---|---|
checkbox |
aria-checked |
combobox |
aria-expanded, aria-controls |
slider |
aria-valuenow, aria-valuemin, aria-valuemax (note: aria-valuemin and aria-valuemax have implicit defaults of 0 and 100) |
option |
aria-selected |
scrollbar |
aria-controls, aria-valuenow |
separator (focusable) |
aria-valuenow |
meter |
aria-valuenow |
heading |
aria-level |
Some roles inherit requirements from ancestor roles. When a role inherits from multiple ancestors and one ancestor marks a property as supported while another marks it as required, the property becomes required on the inheriting role.
In some cases, default values defined in the specification satisfy the requirement automatically, so you may not always need to explicitly set every attribute. However, explicitly providing required attributes is the safest approach.
How to Fix the Problem
-
Identify the ARIA
roleon the element. - Look up the role in the WAI-ARIA Roles documentation to find its required states and properties.
- Add any missing required attributes with appropriate values.
-
Ensure the attribute values are updated dynamically as the widget state changes (e.g., toggling
aria-checkedbetweentrueandfalsewhen a checkbox is clicked).
Alternatively, consider whether a native HTML element could replace the custom ARIA widget. Native elements like <input type="checkbox">, <input type="range">, and <select> handle state management automatically without needing ARIA attributes.
Examples
Incorrect: Checkbox missing aria-checked
<div role="checkbox" tabindex="0">
Accept terms and conditions
</div>
A screen reader announces this as a checkbox, but the user has no way to know if it is checked or unchecked.
Correct: Checkbox with aria-checked
<div role="checkbox" tabindex="0" aria-checked="false">
Accept terms and conditions
</div>
Better: Use a native HTML checkbox
<label>
<input type="checkbox">
Accept terms and conditions
</label>
Incorrect: Slider missing required value attributes
<div role="slider" tabindex="0">
Volume
</div>
Without aria-valuenow, assistive technologies cannot report the current value of the slider.
Correct: Slider with required attributes
<div role="slider" tabindex="0"
aria-valuenow="50"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Volume">
</div>
Incorrect: Combobox missing aria-expanded and aria-controls
<input role="combobox" type="text" aria-label="Search">
Correct: Combobox with required attributes
<input role="combobox" type="text"
aria-label="Search"
aria-expanded="false"
aria-controls="search-listbox">
<ul id="search-listbox" role="listbox" hidden>
<li role="option">Option 1</li>
<li role="option">Option 2</li>
</ul>
Incorrect: Heading missing aria-level
<div role="heading">Section Title</div>
Correct: Heading with aria-level
<div role="heading" aria-level="2">Section Title</div>
Better: Use a native heading element
<h2>Section Title</h2>
The WAI-ARIA specification defines a strict hierarchy for many roles, where parent and child relationships must be maintained for the accessibility semantics to work as intended. For example, an element with role="list" must contain elements with role="listitem", just as a role="menu" must contain role="menuitem" (or related menu item roles). When these required child roles are absent, assistive technologies like screen readers cannot properly convey the structure of the component. A screen reader user navigating a tree view, for instance, needs to understand the parent container, individual items, and sibling relationships — none of which are communicated correctly if the expected child roles are missing.
This issue critically affects users who are blind, deafblind, or have mobility impairments and rely on assistive technologies to navigate and interact with complex widgets. Without the correct role hierarchy, these users may not understand what type of content they’re interacting with, how many items exist, or how to navigate between them.
This rule relates to WCAG 2.0/2.1/2.2 Success Criterion 1.3.1: Info and Relationships (Level A), which requires that information, structure, and relationships conveyed through presentation can be programmatically determined. When ARIA roles lack their required children, the structural relationships are broken and cannot be programmatically determined by assistive technologies.
There are two ways the required parent-child relationship can be established:
- DOM structure: The child elements with the required roles are direct descendants (or appropriate descendants) of the parent element in the DOM.
-
aria-owns: When the DOM hierarchy doesn’t match the intended accessibility tree structure, thearia-ownsattribute can be used on the parent to explicitly associate child elements that exist elsewhere in the DOM.
How to Fix
- Identify the ARIA role on the parent element.
- Consult the WAI-ARIA specification to determine which child roles are required for that parent role.
-
Ensure all direct children (or owned children via
aria-owns) have the correct required roles. - If you cannot add the required child roles, consider whether the parent role is appropriate for your use case, or use native HTML elements that provide these semantics automatically.
Common Parent-Child Role Requirements
| Parent Role | Required Child Role(s) |
|---|---|
list |
listitem |
menu |
menuitem, menuitemcheckbox, or menuitemradio |
menubar |
menuitem, menuitemcheckbox, or menuitemradio |
tablist |
tab |
tree |
treeitem or group |
grid |
row or rowgroup |
table |
row or rowgroup |
row |
cell, columnheader, gridcell, or rowheader |
feed |
article |
Examples
Incorrect: Missing required child roles
This tablist has children that lack the required tab role:
<div role="tablist">
<div>Tab 1</div>
<div>Tab 2</div>
<div>Tab 3</div>
</div>
This list contains <div> elements without the listitem role:
<div role="list">
<div>Apple</div>
<div>Banana</div>
<div>Cherry</div>
</div>
This menu has children with incorrect roles:
<div role="menu">
<div role="option">Cut</div>
<div role="option">Copy</div>
<div role="option">Paste</div>
</div>
Correct: Required child roles present
The tablist now contains children with role="tab":
<div role="tablist">
<div role="tab" aria-selected="true">Tab 1</div>
<div role="tab">Tab 2</div>
<div role="tab">Tab 3</div>
</div>
The list contains children with role="listitem":
<div role="list">
<div role="listitem">Apple</div>
<div role="listitem">Banana</div>
<div role="listitem">Cherry</div>
</div>
The menu contains children with role="menuitem":
<div role="menu">
<div role="menuitem">Cut</div>
<div role="menuitem">Copy</div>
<div role="menuitem">Paste</div>
</div>
Correct: Using aria-owns for children outside the DOM hierarchy
When required children are not direct descendants in the DOM, use aria-owns to establish the relationship:
<div role="tablist" aria-owns="tab1 tab2 tab3">
<!-- Tabs may be rendered elsewhere in the DOM -->
</div>
<div role="tab" id="tab1" aria-selected="true">Tab 1</div>
<div role="tab" id="tab2">Tab 2</div>
<div role="tab" id="tab3">Tab 3</div>
Correct: Using native HTML elements instead
Native HTML elements automatically provide the correct role relationships without any ARIA attributes:
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
Whenever possible, prefer native HTML elements over ARIA roles. Native elements come with built-in semantics, keyboard behavior, and accessibility support that don’t require manual role management.
The WAI-ARIA specification defines a strict hierarchy for many roles. Some roles are only meaningful when they appear as children of particular parent roles. For example, a tab must be owned by a tablist, a listitem must be owned by a list or group, and a menuitem must be owned by a menu or menubar. When these parent-child relationships are missing, the role becomes semantically orphaned — assistive technologies like screen readers cannot communicate the element’s context, position, or purpose to the user.
Why This Matters
This rule relates to WCAG 2.0/2.1/2.2 Success Criterion 1.3.1: Info and Relationships (Level A), which requires that information, structure, and relationships conveyed visually are also available programmatically. When an ARIA role lacks its required parent, the structural relationship is lost in the accessibility tree.
Users most affected include:
- Blind and deafblind users who rely on screen readers. Without the correct parent role, screen readers cannot announce context such as “item 3 of 5 in a list” or “tab 2 of 4.”
- Users with mobility impairments who depend on assistive technology for navigation. Missing parent roles can break expected keyboard interaction patterns for composite widgets like menus, tablists, and trees.
The user impact is critical — an orphaned role can make an entire widget unusable for assistive technology users.
Common Required Parent Roles
Here are some frequently used roles and their required parents:
| Child Role | Required Parent Role(s) |
|---|---|
listitem |
list or group |
tab |
tablist |
tabpanel |
(associated via tablist pattern) |
menuitem, menuitemcheckbox, menuitemradio |
menu, menubar, or group |
treeitem |
tree or group |
row |
table, grid, rowgroup, or treegrid |
cell, gridcell |
row |
columnheader, rowheader |
row |
option |
listbox or group |
How to Fix the Problem
-
Wrap the element in its required parent role. The simplest fix is to ensure the DOM structure reflects the required hierarchy. Place the element inside a container that has the correct parent role.
-
Use
aria-ownswhen DOM structure doesn’t match. If the child element cannot be a DOM descendant of the parent (e.g., due to layout constraints), you can usearia-ownson the parent element to establish the relationship programmatically. Thearia-ownsattribute tells assistive technologies that the referenced element should be treated as a child, regardless of DOM position. -
Check the WAI-ARIA specification for the specific role you’re using to confirm which parent roles are required.
Examples
Incorrect: listitem Without a list Parent
The listitem role requires a parent with role="list" or role="group", but here it sits directly inside a plain div:
<div>
<div role="listitem">Apples</div>
<div role="listitem">Bananas</div>
<div role="listitem">Cherries</div>
</div>
Correct: listitem Inside a list Parent
<div role="list">
<div role="listitem">Apples</div>
<div role="listitem">Bananas</div>
<div role="listitem">Cherries</div>
</div>
Incorrect: tab Without a tablist Parent
<div>
<button role="tab" aria-selected="true">Tab 1</button>
<button role="tab" aria-selected="false">Tab 2</button>
</div>
Correct: tab Inside a tablist Parent
<div role="tablist">
<button role="tab" aria-selected="true">Tab 1</button>
<button role="tab" aria-selected="false">Tab 2</button>
</div>
Correct: Using aria-owns When DOM Structure Differs
When layout constraints prevent nesting the child inside the parent in the DOM, use aria-owns to establish the relationship:
<div role="tablist" aria-owns="tab1 tab2">
<!-- Tabs are elsewhere in the DOM due to layout needs -->
</div>
<div id="tab1" role="tab" aria-selected="true">Tab 1</div>
<div id="tab2" role="tab" aria-selected="false">Tab 2</div>
Incorrect: menuitem Outside a menu
<div>
<button role="menuitem">Cut</button>
<button role="menuitem">Copy</button>
<button role="menuitem">Paste</button>
</div>
Correct: menuitem Inside a menu
<div role="menu">
<button role="menuitem">Cut</button>
<button role="menuitem">Copy</button>
<button role="menuitem">Paste</button>
</div>
Why This Matters
The aria-roledescription attribute lets authors provide a human-readable, localized description of an element’s role. For example, you might use it to describe a role="slider" as “priority picker” so a screen reader announces something more meaningful to the user. However, this attribute only works when the element already has a role that assistive technologies can identify.
When aria-roledescription is placed on an element with no semantic role — like a plain <div>, <span>, or <p> — there is no role for the description to refine. This creates a confusing situation where assistive technologies may announce a description with no context, announce nothing at all, or behave unpredictably. In some cases, this can break accessibility for entire sections of an application.
This issue primarily affects blind users, deafblind users, and users with mobility impairments who rely on screen readers or other assistive technologies to understand and navigate page content. When role information is nonsensical or missing, these users lose the ability to understand what a UI element is and how to interact with it.
Related WCAG Success Criteria
This rule relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that for all user interface components, the name, role, and value can be programmatically determined. When aria-roledescription is applied to an element without a semantic role, the role cannot be properly communicated, violating this criterion.
This applies across WCAG 2.0, 2.1, and 2.2 at Level A, as well as EN 301 549 guideline 9.4.1.2.
How the Rule Works
The axe rule aria-roledescription checks every element that has an aria-roledescription attribute and verifies that the element also has a semantic role. There are three possible outcomes:
-
Fail: The element has no role at all (e.g.,
<div>,<span>,<p>without an explicitroleattribute). These elements have a generic or no implicit role, soaria-roledescriptionhas nothing to describe. -
Pass: The element has a well-supported implicit role (like
<button>,<img>,<nav>) or an explicit role (likerole="combobox"). -
Incomplete (needs review): The element has a role that may not be widely supported by assistive technologies (e.g.,
role="rowgroup"). These need manual testing to verify they work correctly.
How to Fix It
-
Identify elements flagged by the rule — they have
aria-roledescriptionbut no semantic role. -
Decide if the element truly needs
aria-roledescription. In many cases, the solution is simply to remove it. -
If the description is needed, either:
-
Use a semantic HTML element that carries an implicit role (e.g., replace
<div>with<button>). -
Add an explicit
roleattribute to the element so the description has context.
-
Use a semantic HTML element that carries an implicit role (e.g., replace
-
Ensure the
aria-roledescriptionvalue meaningfully refines the role — it should describe a more specific version of what the element is, not contradict it.
Examples
Incorrect: aria-roledescription on elements with no semantic role
These elements have no implicit or explicit role, so aria-roledescription has nothing to describe.
<p aria-roledescription="my paragraph">
This is some text.
</p>
<div aria-roledescription="my container">
Some content here.
</div>
<span aria-roledescription="my label">Name</span>
A <p> has no corresponding ARIA role, and a plain <div> or <span> maps to no role (or the generic generic role). Screen readers cannot use the description meaningfully.
Correct: aria-roledescription on elements with an implicit role
These HTML elements carry built-in semantic roles, so the description refines something real.
<img
aria-roledescription="illustration"
src="diagram.png"
alt="System architecture overview" />
<button aria-roledescription="play control">
Play
</button>
The <img> element has an implicit role of img, and <button> has an implicit role of button. The aria-roledescription values provide more specific descriptions of these roles.
Correct: aria-roledescription on elements with an explicit role
<div
role="combobox"
aria-roledescription="city picker"
aria-expanded="false"
aria-haspopup="listbox">
Select a city
</div>
<div
role="slider"
aria-roledescription="priority selector"
aria-valuenow="3"
aria-valuemin="1"
aria-valuemax="5"
tabindex="0">
</div>
The explicit role attribute provides the semantic foundation, and aria-roledescription adds a more user-friendly label for what that role represents in this specific context.
Incorrect fix: Adding a mismatched role just to satisfy the rule
Don’t add a role that doesn’t match the element’s actual behavior just to pass the check.
<!-- Don't do this -->
<p role="button" aria-roledescription="my paragraph">
This is some text.
</p>
If the element is just a paragraph of text, remove aria-roledescription entirely rather than adding an incorrect role.
Needs review: aria-roledescription on elements with limited role support
Some roles have inconsistent assistive technology support. These will be flagged as needing manual review.
<h1 aria-roledescription="page title">Welcome</h1>
<div role="rowgroup" aria-roledescription="data section">
<!-- row content -->
</div>
The heading role and rowgroup role may not consistently support aria-roledescription across all screen readers. Test these cases manually with actual assistive technologies to confirm the description is announced correctly.
When you assign a role attribute to an HTML element, you are explicitly telling browsers and assistive technologies what that element represents and how users should interact with it. If the value is misspelled, invented, or references an abstract role (like widget, roletype, landmark, or structure), the browser cannot map the element to any known role. The result is that assistive technologies either ignore the role entirely or fall back to the element’s native semantics, which may not reflect your intent.
This is a critical accessibility problem. Users who rely on screen readers — including people who are blind, deafblind, or have mobility impairments — depend on accurate role information to navigate and interact with a page. A button announced as a generic element, or a navigation landmark that isn’t recognized at all, can make an interface confusing or completely unusable.
This rule relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that the role of every user interface component can be programmatically determined. It applies across WCAG 2.0, 2.1, and 2.2, as well as EN 301 549 (Section 9.4.1.2).
How to Fix
-
Check spelling. The most common cause of invalid roles is a simple typo, such as
role="buton"instead ofrole="button". - Use only defined roles. Every value must correspond to an actual role in the WAI-ARIA specification.
-
Never use abstract roles. Abstract roles like
widget,roletype,landmark,structure,input,range,section,sectionhead,select,command, andcompositeexist only as conceptual categories in the spec. They must not be used in markup. -
Consider whether you need
roleat all. Native HTML elements like<button>,<nav>, and<table>already carry implicit roles. Use native elements whenever possible instead of addingroleattributes to generic<div>or<span>elements.
Valid Roles by Category
Here is a summary of valid, non-abstract ARIA roles you can use:
-
Document structure:
application,article,blockquote,caption,code,definition,deletion,document,emphasis,feed,figure,generic,group,heading,img,insertion,list,listitem,mark,math,meter,none,note,paragraph,presentation,separator(when not focusable),strong,subscript,superscript,term,time,toolbar,tooltip -
Landmark:
banner,complementary,contentinfo,form,main,navigation,region,search -
Live region:
alert,log,marquee,status,timer -
Widget:
button,checkbox,gridcell,link,menuitem,menuitemcheckbox,menuitemradio,option,progressbar,radio,scrollbar,searchbox,separator(when focusable),slider,spinbutton,switch,tab,tabpanel,textbox,treeitem -
Composite widget:
combobox,grid,listbox,menu,menubar,radiogroup,tablist,tree,treegrid -
Table:
cell,columnheader,row,rowgroup,rowheader,table -
Window:
alertdialog,dialog
Examples
Incorrect: Misspelled Role
A typo in the role value means assistive technologies won’t recognize it.
<div role="nagivation">
<a href="/">Home</a>
<a href="/about">About</a>
</div>
Incorrect: Made-Up Role
Using a value that doesn’t exist in the ARIA specification.
<div role="footer-container">
<p>© 2024 Example Corp</p>
</div>
Incorrect: Abstract Role
Abstract roles are not allowed in markup.
<div role="widget">
<button>Save</button>
</div>
Correct: Valid ARIA Role
<div role="navigation" aria-label="Main">
<a href="/">Home</a>
<a href="/about">About</a>
</div>
Correct: Using Native HTML Instead of a Role
When a native element already carries the semantics you need, prefer it over a role attribute.
<nav aria-label="Main">
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
Correct: Valid Widget Role
<div role="tablist" aria-label="Settings">
<button role="tab" aria-selected="true" aria-controls="panel-1">General</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">Advanced</button>
</div>
<div role="tabpanel" id="panel-1">General settings content</div>
<div role="tabpanel" id="panel-2" hidden>Advanced settings content</div>
Toggle fields are interactive controls that let users select options, toggle settings, or make choices. When these controls are built using ARIA roles instead of native HTML elements, the browser does not automatically derive a name from a <label> element or inner content the way it would for a native <input type="checkbox">. You must explicitly provide an accessible name so assistive technologies can announce the control’s purpose.
Without an accessible name, a screen reader might announce something like “checkbox, unchecked” with no indication of what the checkbox represents. This makes the interface unusable for people who are blind, deafblind, or who rely on voice control software. Users with mobility impairments who use speech recognition tools also depend on accessible names to target and activate controls by voice.
This rule applies to elements with the following ARIA roles:
-
checkbox -
menuitemcheckbox -
menuitemradio -
radio -
radiogroup -
switch
Related WCAG Success Criteria
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A). This criterion requires that for all user interface components, the name and role can be programmatically determined. Toggle fields that lack an accessible name violate this requirement because assistive technologies cannot convey the control’s purpose to the user.
How to Fix It
You can provide an accessible name for ARIA toggle fields using any of these techniques:
- Inner text content — Place descriptive text inside the element. The browser uses this text as the accessible name.
-
aria-label— Add anaria-labelattribute with a descriptive string directly on the element. -
aria-labelledby— Reference theidof another element that contains the label text. Ensure the referenced element actually exists in the DOM. -
title— Use thetitleattribute as a last resort. While it does provide an accessible name, it is less discoverable for sighted users and not consistently exposed by all assistive technologies.
Important: If you use aria-labelledby, make sure the referenced id matches an element that exists and contains meaningful text. Pointing to a non-existent id results in no accessible name.
Examples
Incorrect: Toggle fields without accessible names
<!-- checkbox with a broken aria-labelledby reference -->
<div role="checkbox" aria-checked="false" aria-labelledby="nonexistent-id"></div>
<!-- menuitemcheckbox with no name -->
<ul role="menu">
<li role="menuitemcheckbox" aria-checked="true"></li>
</ul>
<!-- menuitemradio with no name -->
<ul role="menu">
<li role="menuitemradio" aria-checked="false"></li>
</ul>
<!-- radio with no name -->
<div role="radiogroup">
<div role="radio" aria-checked="false" tabindex="0"></div>
</div>
<!-- switch with no name and empty child spans -->
<div role="switch" aria-checked="true">
<span></span>
<span></span>
</div>
Each of these elements has no text content, no valid aria-label, no working aria-labelledby, and no title. A screen reader cannot announce what they represent.
Correct: Toggle fields with accessible names
<!-- checkbox: name from inner text content -->
<div role="checkbox" aria-checked="false" tabindex="0">
Subscribe to newsletter
</div>
<!-- menuitemcheckbox: name from aria-label -->
<ul role="menu">
<li role="menuitemcheckbox"
aria-checked="true"
aria-label="Word wrap">
</li>
</ul>
<!-- menuitemradio: name from aria-labelledby -->
<p id="font-label">Sans-serif</p>
<ul role="menu">
<li role="menuitemradio"
aria-checked="true"
aria-labelledby="font-label">
</li>
</ul>
<!-- radio: name from title -->
<div role="radiogroup" aria-label="Crust type">
<div role="radio"
aria-checked="false"
tabindex="0"
title="Regular Crust">
</div>
</div>
<!-- switch: name from aria-label -->
<div role="switch"
aria-checked="true"
aria-label="Dark mode"
tabindex="0">
<span>off</span>
<span>on</span>
</div>
Preferred: Use native HTML elements when possible
Native HTML elements come with built-in accessible name mechanisms and keyboard behavior. Whenever possible, prefer them over custom ARIA toggle fields:
<!-- Native checkbox with a label -->
<label>
<input type="checkbox"> Subscribe to newsletter
</label>
<!-- Native radio buttons with a fieldset -->
<fieldset>
<legend>Crust type</legend>
<label><input type="radio" name="crust" value="regular"> Regular Crust</label>
<label><input type="radio" name="crust" value="thin"> Thin Crust</label>
</fieldset>
Using native elements reduces the risk of accessibility issues and eliminates the need to manually manage roles, states, and keyboard interactions.
Tooltips are supplementary text elements that appear when a user hovers over or focuses on a control. They typically provide descriptions, labels, or additional context. When an element has role="tooltip", assistive technologies recognize it as a tooltip and attempt to announce its name to the user.
If a tooltip lacks an accessible name, screen reader users hear something like “tooltip” with no accompanying description. This means they miss the very information the tooltip was designed to provide. Users affected include:
- Blind users who rely entirely on screen readers to access tooltip content.
- Low vision users who may use screen readers in combination with magnification and depend on announced text.
- Mobility-impaired users who navigate with keyboards or alternative input devices and rely on programmatic relationships to understand UI elements.
This rule relates to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have a name that can be programmatically determined. It applies across WCAG 2.0, 2.1, and 2.2, as well as EN 301 549 (guideline 9.4.1.2). Since this is a Level A requirement, it represents the minimum baseline for accessibility compliance.
How to Fix It
Ensure every element with role="tooltip" has a discernible accessible name through one of these methods:
- Inner text content — Place readable text directly inside the tooltip element.
-
aria-labelattribute — Set a non-emptyaria-labelwith a clear description. -
aria-labelledbyattribute — Reference another element’sidthat contains visible, non-empty text. -
titleattribute — Provide a non-emptytitleon the tooltip element (thougharia-labelor inner text are generally preferred).
The accessible name should be concise and clearly describe the information the tooltip is meant to communicate.
Examples
Incorrect: Empty Tooltip with No Accessible Name
The tooltip has no text content and no naming attribute, so screen readers cannot announce anything meaningful.
<button aria-describedby="tip1">Settings</button>
<div role="tooltip" id="tip1"></div>
Incorrect: Empty aria-label
An empty aria-label does not provide an accessible name.
<button aria-describedby="tip2">Save</button>
<div role="tooltip" id="tip2" aria-label=""></div>
Incorrect: aria-labelledby Pointing to a Non-Existent or Empty Element
If the referenced element doesn’t exist or has no text content, the tooltip has no accessible name.
<button aria-describedby="tip3">Delete</button>
<div role="tooltip" id="tip3" aria-labelledby="nonexistent"></div>
<button aria-describedby="tip4">Delete</button>
<div role="tooltip" id="tip4" aria-labelledby="empty-label"></div>
<div id="empty-label"></div>
Correct: Tooltip with Inner Text
The simplest approach — place descriptive text directly inside the tooltip element.
<button aria-describedby="tip5">Save</button>
<div role="tooltip" id="tip5">Save your current progress</div>
Correct: Tooltip with aria-label
Use aria-label when the tooltip’s visual content differs from what you want screen readers to announce, or when the tooltip is styled in a way that doesn’t use direct text content.
<button aria-describedby="tip6">Settings</button>
<div role="tooltip" id="tip6" aria-label="Open application settings"></div>
Correct: Tooltip with aria-labelledby
Reference another element that contains the descriptive text.
<button aria-describedby="tip7">Delete</button>
<div role="tooltip" id="tip7" aria-labelledby="tip7-label"></div>
<span id="tip7-label">Permanently delete this item</span>
Correct: Tooltip with title Attribute
The title attribute provides an accessible name as a fallback, though inner text or aria-label are generally more reliable across assistive technologies.
<button aria-describedby="tip8">Print</button>
<div role="tooltip" id="tip8" title="Print the current document"></div>
ARIA attributes communicate essential information about the state, properties, and roles of interface elements to assistive technologies like screen readers and braille displays. When these attributes contain invalid values, the communication breaks down entirely. A screen reader might ignore the attribute, misrepresent the element’s state, or behave unpredictably — any of which can make content unusable.
This issue has a critical impact on users who are blind, deafblind, or have mobility impairments who rely on assistive technology to navigate and interact with web content. For example, if a checkbox uses aria-checked="ture" instead of aria-checked="true", a screen reader cannot determine whether the checkbox is checked, leaving the user unable to understand the form’s current state.
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that for all user interface components, the name, role, and value can be programmatically determined and set by assistive technologies. Invalid ARIA values violate this criterion because the value cannot be meaningfully determined.
How to Fix It
For every aria- attribute in your markup, confirm that its value:
-
Is spelled correctly — a typo like
"flase"instead of"false"will cause a failure. - Is a permitted value for that specific attribute — each ARIA attribute accepts only certain value types.
- Makes sense in context — the value must be meaningful for the role and state of the element.
Understanding Value Types
Different ARIA attributes accept different types of values. Here are the most common:
-
true/false— Boolean values. Default is typically"false". Example:aria-hidden="true". -
tristate— Accepts"true","false", or"mixed". Example:aria-checked="mixed"for a partially selected checkbox. -
true/false/undefined— Liketrue/false, but"undefined"explicitly indicates the property is not relevant. -
token— One value from a limited set of allowed strings. Example:aria-sortaccepts"ascending","descending","none", or"other". -
token list— A space-separated list of one or more allowed tokens. Example:aria-relevant="additions text". -
ID reference— Theidof another element in the same document. Example:aria-labelledby="heading-1". -
ID reference list— A space-separated list of element IDs. Example:aria-describedby="desc1 desc2". -
integer— A whole number with no fractional part. Example:aria-level="2". -
number— Any real number. Example:aria-valuenow="3.5". -
string— An unconstrained text value. Example:aria-label="Close dialog".
For a complete reference of which values each attribute accepts, consult the WAI-ARIA 1.1 Supported States and Properties.
Watch Out for Common Pitfalls
-
Typos in boolean values —
"ture","flase","yes", and"no"are all invalid for attributes that expect"true"or"false". -
Using wrong tokens — Attributes like
aria-sort,aria-autocomplete, andaria-currentonly accept specific string values. -
Referencing non-existent IDs — If
aria-labelledbypoints to anidthat doesn’t exist in the document, the reference is invalid. -
Implicit defaults — Some roles change the default value of certain properties. For instance,
aria-expandedon acomboboxdefaults to"false"rather than"undefined". Be aware of role-specific defaults.
Examples
Incorrect: Misspelled Boolean Value
<div aria-hidden="flase">
This content should be visible to assistive technology.
</div>
The value "flase" is not a valid boolean. Assistive technologies may not be able to interpret the intended state.
Correct: Properly Spelled Boolean Value
<div aria-hidden="false">
This content is visible to assistive technology.
</div>
Incorrect: Invalid Token Value
<button aria-pressed="yes">
Bold
</button>
The aria-pressed attribute accepts "true", "false", or "mixed" — not "yes".
Correct: Valid Token Value
<button aria-pressed="true">
Bold
</button>
Incorrect: Invalid Tristate Value on a Checkbox
<div role="checkbox" aria-checked="partial" tabindex="0">
Select all items
</div>
The aria-checked attribute on a checkbox role only accepts "true", "false", or "mixed". The value "partial" is not recognized.
Correct: Valid Tristate Value on a Checkbox
<div role="checkbox" aria-checked="mixed" tabindex="0">
Select all items
</div>
Incorrect: Invalid Value for aria-sort
<th aria-sort="alphabetical">Name</th>
The aria-sort attribute only accepts "ascending", "descending", "none", or "other".
Correct: Valid Value for aria-sort
<th aria-sort="ascending">Name</th>
Incorrect: Non-Existent ID Reference
<input type="text" aria-labelledby="username-label">
<!-- No element with id="username-label" exists in the document -->
Correct: Valid ID Reference
<label id="username-label">Username</label>
<input type="text" aria-labelledby="username-label">
What This Rule Checks
The aria-valid-attr-value rule inspects every element that has one or more aria- attributes and verifies that each attribute’s value conforms to the allowed values defined in the WAI-ARIA specification. It checks for correct spelling, valid tokens, proper value types (boolean, integer, ID reference, etc.), and ensures that referenced IDs exist in the document.
When you add an ARIA attribute to an HTML element, the browser exposes that information through the accessibility tree so assistive technologies like screen readers can interpret it. If the attribute name is invalid — whether due to a typo like aria-hiden instead of aria-hidden, or a fabricated attribute like aria-visible that doesn’t exist in the spec — the browser won’t recognize it. The attribute is effectively dead code, and the accessibility enhancement you intended never reaches the user.
This is classified as a critical issue because the consequences can be severe. For example, if you misspell aria-required as aria-requried on a form field, screen reader users won’t be informed that the field is mandatory. If you misspell aria-expanded on a disclosure widget, blind and deafblind users won’t know whether a section is open or closed. Keyboard-only users who rely on screen readers are also affected when interactive states and properties fail to communicate correctly.
Related WCAG Success Criteria
This rule maps to WCAG Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that for all user interface components, the name, role, and states/properties can be programmatically determined. Invalid ARIA attributes fail to communicate states and properties to assistive technologies, directly violating this criterion. This applies across WCAG 2.0, 2.1, and 2.2, as well as EN 301 549 (guideline 9.4.1.2).
How to Fix It
-
Audit your ARIA attributes. Review every attribute in your markup that starts with
aria-and confirm it matches a valid attribute name from the WAI-ARIA specification. -
Check for typos. Common mistakes include
aria-labelled-by(correct:aria-labelledby),aria-hiden(correct:aria-hidden), andaria-discribedby(correct:aria-describedby). -
Remove invented attributes. Attributes like
aria-visible,aria-tooltip, oraria-icondo not exist in the WAI-ARIA spec and will have no effect. -
Use tooling. IDE extensions, linters (like
eslint-plugin-jsx-a11y), and the axe DevTools browser extension can catch invalid ARIA attribute names during development.
Common Valid ARIA Attributes
Here are some frequently used ARIA attributes for reference:
-
Widget attributes:
aria-checked,aria-disabled,aria-expanded,aria-hidden,aria-label,aria-pressed,aria-readonly,aria-required,aria-selected,aria-valuenow -
Live region attributes:
aria-live,aria-atomic,aria-relevant,aria-busy -
Relationship attributes:
aria-labelledby,aria-describedby,aria-controls,aria-owns,aria-flowto -
Drag-and-drop attributes:
aria-dropeffect,aria-grabbed
Examples
Incorrect: Misspelled ARIA Attribute
<button aria-expandd="false">Show details</button>
The attribute aria-expandd is not a valid ARIA attribute. Screen readers will not announce the expanded/collapsed state of this button.
Incorrect: Non-Existent ARIA Attribute
<div aria-visible="true">Important announcement</div>
The attribute aria-visible does not exist in the WAI-ARIA specification. It will be completely ignored by assistive technologies.
Incorrect: Typo in a Relationship Attribute
<input type="text" aria-discribedby="help-text">
<p id="help-text">Enter your full name as it appears on your ID.</p>
The attribute aria-discribedby is a misspelling of aria-describedby. The input will not be associated with the help text for screen reader users.
Correct: Properly Spelled ARIA Attributes
<button aria-expanded="false">Show details</button>
<div aria-hidden="true">Decorative content</div>
<input type="text" aria-describedby="help-text">
<p id="help-text">Enter your full name as it appears on your ID.</p>
Each of these examples uses a valid, correctly spelled ARIA attribute that browsers and assistive technologies will recognize and process as intended.
When an <audio> element lacks a captions track, all of the information it conveys — dialogue, narration, sound effects, musical cues, and speaker identification — becomes completely inaccessible to users who are deaf or deafblind. This is considered a critical accessibility issue because it blocks entire groups of users from accessing content.
This rule relates to WCAG Success Criterion 1.2.1: Audio-only and Video-only (Prerecorded) (Level A), which requires that a text alternative be provided for prerecorded audio-only content. It also falls under Section 508 requirements and EN 301 549. Level A criteria represent the most fundamental accessibility requirements — failing to meet them means significant barriers exist for users with disabilities.
Captions vs. Subtitles
It’s important to understand that captions and subtitles are not the same thing:
-
Captions (
kind="captions") are designed for deaf and hard-of-hearing users. They include dialogue, speaker identification, sound effects (e.g., “[door slams]”), musical cues (e.g., “[soft piano music]”), and other meaningful audio information. -
Subtitles (
kind="subtitles") are language translations intended for hearing users who don’t understand the spoken language. They typically include only dialogue and narration.
Because of this distinction, you must use kind="captions", not kind="subtitles", to satisfy this rule.
How to Fix It
-
Create a captions file (typically in WebVTT
.vttformat) that includes all meaningful audio information: who is speaking, what they say, and relevant non-speech sounds. -
Add a
<track>element inside your<audio>element. -
Set the
kindattribute to"captions". -
Set the
srcattribute to the path of your captions file. -
Use the
srclangattribute to specify the language of the captions. -
Use the
labelattribute to give the track a human-readable name.
While only src is technically required on a <track> element, including kind, srclang, and label is strongly recommended for clarity and proper functionality.
Examples
Incorrect: <audio> with no captions track
<audio controls>
<source src="podcast.mp3" type="audio/mp3">
</audio>
This fails the rule because there is no <track> element providing captions.
Incorrect: <track> with wrong kind value
<audio controls>
<source src="podcast.mp3" type="audio/mp3">
<track src="subs_en.vtt" kind="subtitles" srclang="en" label="English">
</audio>
This fails because kind="subtitles" does not satisfy the captions requirement. Subtitles are not a substitute for captions.
Correct: <audio> with a captions track
<audio controls>
<source src="podcast.mp3" type="audio/mp3">
<track src="captions_en.vtt" kind="captions" srclang="en" label="English Captions">
</audio>
Correct: <audio> with multiple caption tracks for different languages
<audio controls>
<source src="interview.mp3" type="audio/mp3">
<track src="captions_en.vtt" kind="captions" srclang="en" label="English Captions">
<track src="captions_es.vtt" kind="captions" srclang="es" label="Subtítulos en español">
</audio>
Providing captions in multiple languages ensures broader accessibility and is especially helpful when your audience speaks different languages.
Example WebVTT captions file
A basic captions_en.vtt file might look like this:
WEBVTT
00:00:01.000 --> 00:00:04.000
[Upbeat intro music]
00:00:04.500 --> 00:00:07.000
Host: Welcome to the show, everyone.
00:00:07.500 --> 00:00:10.000
Guest: Thanks for having me!
00:00:10.500 --> 00:00:12.000
[Audience applause]
Notice how the captions include speaker identification (Host:, Guest:), non-speech sounds ([Upbeat intro music], [Audience applause]), and the full dialogue. This level of detail is what makes captions effective for deaf and deafblind users.
The <blink> element was once used to draw attention to content by making it flash repeatedly. While it may have seemed like an eye-catching effect, it creates serious accessibility barriers for multiple groups of users. The element has been deprecated from the HTML specification, and although most modern browsers no longer render the blinking effect, the element should still be removed from your markup to ensure compliance and avoid issues in any environment that might still support it.
Why this is an accessibility problem
Blinking content affects several groups of users:
- Users with cognitive disabilities may find blinking text distracting or incomprehensible. The constant flashing can make it very difficult to focus on and understand the content.
- Users with low vision struggle to read text that appears and disappears rapidly. The intermittent visibility makes the content effectively unreadable.
- Users with motor disabilities may have difficulty clicking on blinking links or buttons, since the target is not consistently visible and requires precise timing to activate.
- Users with photosensitive conditions can experience discomfort or, in extreme cases, seizures from flashing content, depending on the frequency.
This rule relates to WCAG 2.2 Success Criterion 2.2.2: Pause, Stop, Hide (Level A), which requires that for any moving, blinking, or scrolling content that starts automatically and lasts more than five seconds, users must be able to pause, stop, or hide it. Since the <blink> element provides no mechanism for users to control the flashing, it fails this criterion outright.
This rule also applies to Section 508 §1194.22(j), which states that pages must be designed to avoid causing the screen to flicker with a frequency greater than 2 Hz and lower than 55 Hz.
How to fix it
-
Remove all
<blink>elements from your HTML. Replace them with standard elements like<span>,<strong>, or<em>. -
Remove
text-decoration: blink;from your CSS, as this is the CSS equivalent of the<blink>element. - Use alternative emphasis techniques to make content stand out — bold text, color, larger font size, borders, background highlights, or icons.
Important: Many modern browsers silently ignore the <blink> element, so the text won’t visually blink even though the element is present in the source code. Don’t rely on how the page looks in the browser to determine whether <blink> tags exist. Always check the actual HTML source.
Examples
Incorrect: using the <blink> element
<p><blink>Moving Sale Thursday!</blink></p>
This causes the text to flash on and off (in browsers that support it), making it inaccessible.
Incorrect: using text-decoration: blink in CSS
<style>
.attention {
text-decoration: blink;
}
</style>
<h2 class="attention">Limited Time Offer!</h2>
The CSS text-decoration: blink value achieves the same inaccessible effect as the <blink> element.
Correct: using static visual emphasis
<p><strong>Moving Sale Thursday!</strong></p>
Using <strong> makes the text bold and conveys emphasis to screen readers without any flashing.
Correct: using CSS for visual emphasis without blinking
<style>
.highlight {
background-color: #fff3cd;
border-left: 4px solid #ffc107;
padding: 8px 12px;
font-weight: bold;
}
</style>
<p class="highlight">Limited Time Offer!</p>
This draws attention to the content using color, a border, and bold text — all without any flashing or blinking, keeping the content readable and accessible for everyone.
When a button lacks an accessible name, assistive technologies like screen readers can only announce it generically — for example, as “button” — with no indication of its purpose. This is a critical barrier for people who are blind or deafblind, as they rely entirely on programmatically determined names to understand and interact with interface controls. A sighted user might infer a button’s purpose from an icon or visual context, but without a text-based name, that information is completely lost to assistive technology users.
This rule maps to WCAG 2.0, 2.1, and 2.2 Success Criterion 4.1.2: Name, Role, Value (Level A), which requires that all user interface components have a name that can be programmatically determined. It is also covered by Section 508, EN 301 549 (9.4.1.2), and Trusted Tester guidelines, which require that the purpose of every link and button be determinable from its accessible name, description, or context.
How to fix it
Ensure every <button> element or element with role="button" has an accessible name through one of these methods:
- Visible text content inside the button element.
-
A non-empty
aria-labelattribute that describes the button’s purpose. -
An
aria-labelledbyattribute that references an element containing visible, non-empty text. -
A
titleattribute (use as a last resort, sincetitletooltips are inconsistently exposed across devices).
If a button is purely decorative and should be hidden from assistive technologies, you can assign role="presentation" or role="none" and remove it from the tab order with tabindex="-1". However, this is rare for interactive buttons.
Common mistakes to avoid
-
Leaving a
<button>element completely empty. -
Using only a
valueattribute on a<button>— unlike<input>elements, thevalueattribute on<button>does not provide an accessible name. -
Setting
aria-labelto an empty string (aria-label=""). -
Pointing
aria-labelledbyto an element that doesn’t exist or contains no text. - Using only an icon or image inside a button without providing alternative text.
Examples
Incorrect: empty button
<button id="search"></button>
A screen reader announces this as “button” with no indication of its purpose.
Incorrect: button with only a value attribute
<button id="submit" value="Submit"></button>
The value attribute does not set the accessible name for <button> elements.
Incorrect: empty aria-label
<button id="close" aria-label=""></button>
An empty aria-label results in no accessible name.
Incorrect: aria-labelledby pointing to a missing or empty element
<button id="save" aria-labelledby="save-label"></button>
<div id="save-label"></div>
The referenced element exists but contains no text, so the button has no accessible name.
Correct: button with visible text
<button>Submit order</button>
Correct: icon button with aria-label
<button aria-label="Close dialog">
<svg aria-hidden="true" focusable="false">
<use href="#icon-close"></use>
</svg>
</button>
The aria-label provides the accessible name, while aria-hidden="true" on the SVG prevents duplicate announcements.
Correct: button labeled by another element
<h2 id="section-title">Shopping cart</h2>
<button aria-labelledby="section-title">
<svg aria-hidden="true" focusable="false">
<use href="#icon-arrow"></use>
</svg>
</button>
The button’s accessible name is drawn from the referenced heading text.
Correct: button with aria-label and visible text
<button aria-label="Search products">Search</button>
When both aria-label and inner text are present, aria-label takes precedence as the accessible name. Use this when you need a more descriptive name than what the visible text alone conveys.
Correct: button with title (last resort)
<button title="Print this page">
<svg aria-hidden="true" focusable="false">
<use href="#icon-print"></use>
</svg>
</button>
The title attribute provides an accessible name, but visible text or aria-label are preferred because title tooltips may not be available to touch-screen or keyboard-only users.
Websites typically repeat navigation links, branding, and other interface elements across every page. While sighted mouse users can visually scan past these blocks and click wherever they want, keyboard-only users and screen reader users must navigate through every interactive element sequentially. Without a bypass mechanism, a keyboard user might need to press Tab dozens of times just to reach the primary content on each new page they visit. For users with severe motor impairments, this can take several minutes per page and cause fatigue or physical pain. Even users with less severe limitations will experience frustrating delays compared to mouse users, who can reach any link in a second or two.
Screen reader users also benefit significantly from bypass mechanisms. Landmarks like <main>, <nav>, and <header> allow screen readers to present a structural outline of the page, enabling users to jump directly to the section they need. Headings serve a similar purpose — screen reader users can navigate by heading level to quickly locate the main content area.
This rule maps to WCAG 2.4.1 Bypass Blocks (Level A), which requires that a mechanism is available to bypass blocks of content repeated on multiple pages. It is also required by Section 508 (specifically §1194.22(o)), the Trusted Tester guidelines, and EN 301 549. Because it is a Level A requirement, it represents the minimum baseline for accessibility compliance.
How the Rule Works
The axe bypass rule checks that a page includes at least one of the following:
-
A landmark region (such as
<main>,<nav>,<header>, or<footer>) -
A heading (an
<h1>through<h6>element) - An internal skip link (an anchor link that points to a location further down the page)
If none of these are present, the rule flags the page as a failure.
How to Fix It
The best approach is to use HTML5 landmark elements to structure your page. At a minimum, include a <main> element that wraps the primary content of the page. You should also use <header>, <nav>, and <footer> to identify other common sections. A page should have only one <main> landmark.
Additionally, consider adding a skip navigation link as the very first focusable element on the page. This provides an immediate shortcut for keyboard users who don’t use screen readers and may not be able to navigate by landmarks.
Prefer native HTML5 elements over their ARIA equivalents. For example, use <main> rather than <div role="main">. Native elements are better supported and require less code.
Examples
Incorrect: No Landmarks, Headings, or Skip Links
This page has no structural landmarks, no headings, and no skip link. Keyboard users must tab through every element to reach the content.
<div class="header">
<div class="logo">My Site</div>
<div class="nav">
<a href="/home">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</div>
<div class="content">
<p>This is the main content of the page.</p>
</div>
<div class="footer">
<p>Footer information</p>
</div>
Correct: Using HTML5 Landmark Elements
Replacing generic <div> wrappers with semantic HTML5 elements gives the page proper structure that assistive technologies can use for navigation.
<header>
<div class="logo">My Site</div>
<nav>
<a href="/home">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<main>
<h1>Welcome</h1>
<p>This is the main content of the page.</p>
<section>
<h2>Latest News</h2>
<p>Section content here.</p>
</section>
</main>
<footer>
<p>Footer information</p>
</footer>
Correct: Adding a Skip Navigation Link
A skip link gives keyboard users an immediate way to bypass repeated content. It is typically visually hidden until it receives focus.
<body>
<a class="skip-link" href="#main-content">Skip to main content</a>
<header>
<nav>
<a href="/home">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<main id="main-content">
<h1>Page Title</h1>
<p>This is the main content of the page.</p>
</main>
<footer>
<p>Footer information</p>
</footer>
</body>
.skip-link {
position: absolute;
left: -9999px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
}
.skip-link:focus {
position: static;
width: auto;
height: auto;
overflow: visible;
}
When the skip link receives keyboard focus, it becomes visible, and pressing Enter moves focus directly to the <main> element. Combined with proper landmark elements, this gives all users fast, reliable access to the page’s primary content.
Ready to validate your sites?
Start your free trial today.