HTML Guide
The role attribute exposes an element’s purpose to assistive technologies. ARIA defines a fixed set of role values; sidebar is not among them, so validators report a bad value. Sidebars typically contain related or ancillary content, which maps to the complementary landmark role. In HTML, the <aside> element already represents this concept and implicitly maps to the complementary role.
Leaving an invalid role harms accessibility because screen readers may ignore the landmark or misreport it, and automated tools can’t build a reliable landmarks map. Standards compliance also matters for consistent behavior across browsers and assistive tech.
To fix it:
-
Replace
role="sidebar"withrole="complementary"on a generic container; add an accessible name witharia-labelledbyoraria-labelwhen multiple complementary regions exist. -
Prefer
<aside>for semantic HTML. It implicitly has thecomplementaryrole; add a label when there is more than one<aside>. -
Do not add
role="complementary"to<aside>unless you need to override something; duplicate roles are unnecessary. -
If the area is site-wide navigation, use
<nav>orrole="navigation"instead; choose the role that best matches the intent.
Examples
Invalid: non-existent ARIA role
<div role="sidebar">
<!-- Related links and promos -->
</div>
Fixed: use the complementary role on a generic container
<div role="complementary" aria-labelledby="sidebar-title">
<h2 id="sidebar-title">Related</h2>
<ul>
<li><a href="/guide-a">Guide A</a></li>
<li><a href="/guide-b">Guide B</a></li>
</ul>
</div>
Fixed: use semantic HTML with aside (implicit complementary)
<aside aria-labelledby="sidebar-title">
<h2 id="sidebar-title">Related</h2>
<ul>
<li><a href="/guide-a">Guide A</a></li>
<li><a href="/guide-b">Guide B</a></li>
</ul>
</aside>
Multiple sidebars: ensure unique, descriptive labels
<aside aria-labelledby="filters-title">
<h2 id="filters-title">Filter results</h2>
<!-- filter controls -->
</aside>
<aside aria-labelledby="related-title">
<h2 id="related-title">Related articles</h2>
<!-- related links -->
</aside>
When it’s actually navigation: use the navigation landmark
<nav aria-label="Section navigation">
<ul>
<li><a href="#intro">Intro</a></li>
<li><a href="#examples">Examples</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
Tips:
-
Use
<aside>for tangential content; it’s the simplest, standards-based approach. - Provide an accessible name when more than one complementary region is present.
-
Avoid inventing ARIA roles; stick to defined values like
banner,main,navigation,complementary,contentinfo, and others.
Learn more:
Last reviewed: February 13, 2026
Related W3C validator issues
ARIA defines a fixed set of role values that user agents and assistive technologies understand. sidebar is not in that set, so role="sidebar" fails conformance checking and gives unreliable signals to screen readers. Using a valid role or the correct HTML element improves accessibility, ensures consistent behavior across browsers and AT, and keeps your markup standards‑compliant.
Sidebars typically contain tangential or ancillary content (e.g., related links, promos, author info). The ARIA role that matches that meaning is complementary. In HTML, the semantic element for the same concept is aside, which by default maps to the complementary landmark in accessibility APIs. Prefer native semantics first: use <aside> when possible. Only add role="complementary" when you can’t change the element type or when you need an explicit landmark for non-semantic containers.
How to fix:
- If the element is a sidebar: change <div role="sidebar"> to <aside> (preferred), or to <div role="complementary">.
- Ensure each page has at most one primary main region and that complementary regions are not essential to understanding the main content.
- Provide an accessible name for the complementary region when multiple exist, using aria-label or aria-labelledby, to help users navigate landmarks.
Examples
Triggers the validator error
<div role="sidebar">
<!-- Sidebar content -->
</div>
Fixed: use the semantic element (preferred)
<aside aria-label="Related articles">
<!-- Sidebar content -->
</aside>
Fixed: keep the container, apply a valid role
<div role="complementary" aria-label="Related articles">
<!-- Sidebar content -->
</div>
Full document example with two sidebars (each labeled)
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sidebar Landmarks Example</title>
</head>
<body>
<header>
<h1>News Today</h1>
</header>
<main id="main">
<article>
<h2>Main Story</h2>
<p>...</p>
</article>
</main>
<aside aria-label="Trending topics">
<ul>
<li>Science</li>
<li>Politics</li>
<li>Sports</li>
</ul>
</aside>
<div role="complementary" aria-labelledby="sponsor-title">
<h2 id="sponsor-title">Sponsored</h2>
<p>Ad content</p>
</div>
<footer>
<p>© 2026</p>
</footer>
</body>
</html>
Notes:
- Do not invent ARIA roles (e.g., sidebar, hero, footer-nav). Use defined roles like complementary, navigation, banner, contentinfo, and main.
- Prefer native HTML elements (aside, nav, header, footer, main) over generic containers with roles.
- Label multiple complementary landmarks to make them distinguishable in screen reader landmark lists.
The dialog element does not require or permit a role="dialog" attribute according to HTML standards.
The <dialog> element has an implicit ARIA role of dialog, so adding role="dialog" is redundant and not valid per the specification. Instead, simply use the <dialog> element without an explicit role attribute.
Details:
According to the WHATWG HTML standard and ARIA specification, native <dialog> elements automatically have the correct role. Adding role="dialog" can cause HTML validation errors, as the validator interprets this as a misuse or redundancy.
Correct usage:
<dialog>
<p>This is a dialog box.</p>
<button>Close</button>
</dialog>
Incorrect usage (causes validation error):
<dialog role="dialog">
<p>This is a dialog box.</p>
<button>Close</button>
</dialog>
Removing the role="dialog" attribute resolves the W3C validation issue while maintaining accessibility.
The validator error occurs when an element such as an a, button, or custom widget includes aria-controls="" (empty) or whitespace-only. The aria-controls attribute takes one or more space-separated id values (IDREFS). Each referenced id must exist exactly once in the same document. Leaving it empty violates the ARIA and HTML requirements and provides no usable relationship for assistive technologies.
Why this matters:
- Accessibility: Screen readers rely on aria-controls to announce relationships between controls and controlled regions (e.g., a toggle and its panel). An empty value misleads AT or adds noise.
- Standards compliance: HTML and ARIA require at least one non-whitespace id. Empty values cause validation failures.
- Robustness: Incorrect references can confuse scripts and future maintainers, and break behavior when IDs change.
How to fix it:
- Only add aria-controls when the element truly controls another region (show/hide, sort, update).
- Ensure the controlled element has a unique id.
- Set aria-controls to that id (or multiple space-separated IDs).
- Keep the reference in sync if IDs change.
- If nothing is controlled, remove aria-controls entirely.
Examples
Invalid: empty aria-controls (triggers the error)
<a href="#" aria-controls="">Toggle details</a>
Valid: control a single region
<div id="details-panel" hidden>
Some details...
</div>
<a href="#details-panel" aria-controls="details-panel">Toggle details</a>
Valid: control multiple regions (space-separated IDs)
<section id="filters" hidden>...</section>
<section id="results" hidden>...</section>
<button type="button" aria-controls="filters results">Show filters and results</button>
Valid: remove when not needed
<a href="#">Toggle details</a>
Minimal complete document with proper usage
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>aria-controls Example</title>
</head>
<body>
<button type="button" aria-controls="info" aria-expanded="false">Toggle info</button>
<div id="info" hidden>
Extra information.
</div>
<script>
const btn = document.querySelector('button');
const panel = document.getElementById(btn.getAttribute('aria-controls'));
btn.addEventListener('click', () => {
const expanded = btn.getAttribute('aria-expanded') === 'true';
btn.setAttribute('aria-expanded', String(!expanded));
panel.hidden = expanded;
});
</script>
</body>
</html>
Tips:
- Use aria-controls for functional relationships (control affects content), not just visual proximity.
- Combine with aria-expanded when toggling visibility to convey state.
- Verify that every id in aria-controls exists and is unique; avoid dynamic mismatches created by templating or component reuse.
The icon value for attribute role is not a valid ARIA role.
ARIA Roles: Overview
ARIA roles are used to enhance accessibility by clearly defining the role and purpose of an element for assistive technologies. However, there are defined roles that you need to adhere to:
- Standard roles include button, checkbox, alert, dialog, img, etc.
- There is no ARIA role named icon.
Solution
To fix the issue, you need to use a valid ARIA role that accurately describes the purpose of the span element. If your intention is to convey that the span contains an icon, you might want to reconsider whether you need a role at all. Often, purely decorative elements should not have a role, or you might use an img role if it conveys a meaningful image.
Here’s how you can address this:
-
No ARIA role (if purely decorative): If the icon is purely decorative and does not add meaningful content to your page, you should remove the role attribute entirely.
<span class="icon"></span> -
Using img role (if it represents an image): If the span represents an image or an icon that conveys meaningful information, you can use role="img" and provide a proper aria-label.
<span class="icon" role="img" aria-label="Icon Description"></span> -
Using an appropriate role (if interactive): If the icon is part of an interactive element, you might need a different role. For instance, if the icon is inside a button:
<button> <span class="icon" aria-hidden="true"></span> Button text </button>Here, aria-hidden="true" is used to hide the decorative icon from screen readers as the text provides the necessary context.
The attribute aria-hidden must have a value of "true" (without extra quotes) or "false", not "\"true\"" (with double quotes inside the value).
The aria-hidden attribute is used to indicate whether an element and its children should be accessible to assistive technologies (like screen readers). Valid values are the strings "true" or "false" (without embedded quotation marks). Using extra quotation marks causes the validator to flag a bad value because the attribute’s value is interpreted literally.
Incorrect Example:
<div aria-hidden='"true"'>This is hidden from assistive tech</div>
Correct Example:
<div aria-hidden="true">This is hidden from assistive tech</div>
or
<div aria-hidden="false">This is visible to assistive tech</div>
Remove any extra quotation marks around your attribute value to resolve the error.
Using the <aside> element will automatically communicate a section has a role of complementary, so specifying the role="complementary" is redundant.
There can only be one visible <main> element in a document. If more are needed (for example for switching between them with JavaScript), only one can be visible, the others should be hidden toggling the hidden attribute.
Example of 2 main elements, where only one is visible:
<main>
<h1>Active main element</h1>
<!-- content -->
</main>
<main hidden>
<h1>Hidden main element</h1>
<!-- content -->
</main>
An a element with both an href attribute and aria-disabled="true" is invalid; either remove aria-disabled or the href attribute.
The aria-disabled attribute is used for interactive elements to indicate that the element is perceivable as disabled by assistive technologies. However, using aria-disabled="true" in combination with an href attribute on an a element is not valid, because the link remains actionable for both user agents and assistive devices. Instead, if a link should appear disabled, you should remove the href attribute, use CSS for styling, and optionally use aria-disabled="true". If you need the element to always act as a link, avoid aria-disabled and control user access through application logic.
Incorrect:
<a href="page.html" aria-disabled="true">Visit Page</a>
Correct—Option 1: Remove aria-disabled, keep link active
<a href="page.html">Visit Page</a>
Correct—Option 2: Remove href, use aria-disabled, for non-actionable item
<a aria-disabled="true" tabindex="-1" style="pointer-events: none; color: gray;">Visit Page</a>
In the second correct example, setting tabindex="-1" prevents keyboard navigation, and pointer-events: none; makes the link unclickable, while aria-disabled="true" makes the disabled state accessible.
A role="columnheader" element must be a child of or associated with a role="row" element.
In HTML, ARIA roles such as columnheader are used to improve accessibility for assistive technologies. According to the ARIA specification, a columnheader role should appear inside an element with role="row", which itself should be inside an element with role="table" or role="grid". This structure mimics how native tables are constructed with <th> elements inside <tr>s.
Correct structure:
- role="table" or role="grid" contains one or more elements with role="row".
- Each role="row" contains one or more elements with role="columnheader" (or role="cell").
Example using ARIA roles for a simple table:
<div role="table" aria-label="Sample Table">
<div role="row">
<div role="columnheader">Name</div>
<div role="columnheader">Age</div>
</div>
<div role="row">
<div role="cell">Alice</div>
<div role="cell">30</div>
</div>
</div>
Best practice:
Whenever possible, use native table elements, which have built-in roles and accessibility, reducing the chance of ARIA misuse.
Example using native table markup:
<table>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
<tr>
<td>Alice</td>
<td>30</td>
</tr>
</table>
Ensure that any element with role="columnheader" is always contained within a parent with role="row". Avoid placing role="columnheader" directly inside a container without the appropriate role="row" ancestor.
To fix this issue, ensure that an element with role="listitem" is contained within an element with role="list" or role="group". Here’s how you can structure your HTML correctly:
Incorrect Example
<div role="listitem">Item 1</div>
<div role="listitem">Item 2</div>
Correct Example
<div role="list">
<div role="listitem">Item 1</div>
<div role="listitem">Item 2</div>
</div>
Alternatively, you can use role="group" if it’s a nested list.
Correct Example with Nested List
<div role="list">
<div role="listitem">Item 1</div>
<div role="group">
<div role="listitem">Item 1.1</div>
<div role="listitem">Item 1.2</div>
</div>
<div role="listitem">Item 2</div>
</div>
This ensures that the role="listitem" elements are correctly contained.