HTML Guides for role
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The W3C HTML specification restricts which ARIA roles can be used on specific elements. An li element already carries the implicit role of listitem when it's a child of a ul or ol, so adding an explicit role is often unnecessary. The group role, as defined in the WAI-ARIA specification, is meant for container elements that form a logical collection of related items — similar to how a fieldset groups form controls. Applying group to an li element contradicts the element's purpose as an individual item within a list.
This matters for several reasons. Assistive technologies like screen readers rely on ARIA roles to communicate the structure and purpose of content to users. When an li element is marked with role="group", it overrides the native listitem semantics, potentially confusing users who depend on these tools. A screen reader might announce the element as a group container rather than a list item, breaking the expected navigation pattern of the list. Browsers and assistive technologies are optimized around correct role usage, so invalid combinations can lead to unpredictable behavior.
When you might actually need group
If your intent is to group a set of related elements together, the group role belongs on a wrapping container — not on the individual items inside it. For example, you might use role="group" on a div or a ul that contains a subset of related controls within a larger interface. If an li contains nested interactive content that needs to be grouped, wrap that content in an inner container with the group role instead.
How to fix it
- Remove the role entirely if the
liis a standard list item inside aulorol. The browser already provides the correctlistitemsemantics. - Move the
grouprole to a container element if you genuinely need to create a labeled group of related items. - Use a different valid role if the
liserves a non-standard purpose, such asmenuitem,option,tab,treeitem, orpresentation, depending on the widget pattern you're building.
Examples
Incorrect: group role on li elements
<ul>
<li role="group">Fruits</li>
<li role="group">Vegetables</li>
<li role="group">Grains</li>
</ul>
This triggers the validation error because group is not a permitted role for li.
Correct: no explicit role needed
<ul>
<li>Fruits</li>
<li>Vegetables</li>
<li>Grains</li>
</ul>
Each li inside a ul automatically has the listitem role. No additional markup is needed.
Correct: using group on a container element
If you need to group related items with a label, apply the group role to the container:
<div role="group" aria-labelledby="food-heading">
<h2 id="food-heading">Food Categories</h2>
<ul>
<li>Fruits</li>
<li>Vegetables</li>
<li>Grains</li>
</ul>
</div>
Correct: nested groups within list items
If each list item contains a group of related controls, place the group role on an inner wrapper:
<ul>
<li>
<div role="group" aria-label="Text formatting">
<button>Bold</button>
<button>Italic</button>
<button>Underline</button>
</div>
</li>
<li>
<div role="group" aria-label="Text alignment">
<button>Left</button>
<button>Center</button>
<button>Right</button>
</div>
</li>
</ul>
This preserves the list structure while correctly applying the group role to containers of related widgets. The aria-label attribute gives each group an accessible name, which is recommended when using role="group".
The WAI-ARIA specification defines a specific set of role values that assistive technologies like screen readers understand. These include roles such as button, checkbox, alert, dialog, img, navigation, banner, and many others. The value "icon" is not among them. When a browser or assistive technology encounters an unrecognized role, it cannot determine the element's purpose, which defeats the goal of using ARIA in the first place.
This is primarily an accessibility problem. Screen readers rely on valid ARIA roles to communicate the nature of elements to users. An invalid role like "icon" is either ignored or causes unpredictable behavior, leaving users of assistive technologies without the context they need. It's also a standards compliance issue — the W3C validator flags this because the HTML specification requires role values to match roles defined in the ARIA specification.
The fix depends on the purpose of the element:
- Decorative icons (that don't convey information): Remove the
roleattribute entirely, or usearia-hidden="true"to explicitly hide the element from the accessibility tree. - Meaningful icons (that convey information visually): Use
role="img"along with anaria-labelto provide a text alternative. - Icons inside interactive elements: Hide the icon with
aria-hidden="true"and ensure the parent interactive element has an accessible name through visible text or anaria-label.
Examples
❌ Invalid: Using the non-existent "icon" role
<span class="icon" role="icon"></span>
This triggers the validation error because "icon" is not a valid ARIA role.
✅ Fixed: Decorative icon with no role
If the icon is purely decorative and doesn't convey any meaning (e.g., it's next to text that already describes the action), simply remove the role attribute. Adding aria-hidden="true" ensures screen readers skip over it completely.
<span class="icon" aria-hidden="true"></span>
✅ Fixed: Meaningful icon using role="img"
If the icon conveys meaningful information that isn't available through surrounding text, use role="img" and provide a descriptive aria-label:
<span class="icon-warning" role="img" aria-label="Warning"></span>
This tells assistive technologies that the element represents an image and gives it an accessible name of "Warning."
✅ Fixed: Icon inside a button
When an icon is placed inside an interactive element like a button, hide the icon from the accessibility tree and let the button's text or label provide the meaning:
<button>
<span class="icon-save" aria-hidden="true"></span>
Save
</button>
If the button has no visible text (an icon-only button), provide an aria-label on the button itself:
<button aria-label="Save">
<span class="icon-save" aria-hidden="true"></span>
</button>
✅ Fixed: Icon using an <img> element instead
If you're using an actual image file for the icon, consider using a semantic <img> element, which has a built-in img role:
<img src="icon-alert.svg" alt="Alert" class="icon">
For decorative image icons, use an empty alt attribute:
<img src="icon-decorative.svg" alt="" class="icon">
The role="img" attribute is not allowed on the article element because article already has an implicit ARIA role of article, and img is not among the roles permitted on this element.
The article element represents a self-contained composition in a document, such as a blog post, a news story, or a forum comment. Its implicit ARIA role is article. According to the ARIA in HTML specification, the article element can only use a limited set of roles: application, document, feed, main, none, presentation, or region. The img role is not in that list.
The img role marks up a collection of elements that together convey a single image, so assistive technologies announce them as one graphic with an accessible name. That meaning conflicts with article, which describes a standalone block of content. If you need img semantics, apply the role to a generic div and give it a label, or use a real <img> element. If you need the semantics of article, keep the element and remove the role.
Example with the issue
<article role="img">
<span>★</span>
<span>★</span>
<span>★</span>
</article>
How to fix it
If you are presenting several elements as one image, use a div with a descriptive label:
<div role="img" aria-label="Rated 3 out of 5 stars">
<span>★</span>
<span>★</span>
<span>★</span>
</div>
If you need the semantics of article, drop the conflicting role:
<article>
<h2>Latest news</h2>
<p>Content of the article.</p>
</article>
The section element carries implicit ARIA semantics — it maps to role="region" when given an accessible name. According to the ARIA in HTML specification, each HTML element has a set of roles it is allowed to assume, and list is not among the permitted roles for section. When the W3C validator encounters role="list" on a section, it flags this as a bad value because the role conflicts with the element's intended purpose as a sectioning landmark.
This matters for several reasons. First, assistive technologies like screen readers rely on the correct pairing of elements and roles to convey page structure. A section announced as a list creates a confusing, contradictory experience — the underlying DOM says "this is a document section" while the ARIA role says "this is a list." Second, browsers may handle conflicting semantics unpredictably, leading to inconsistent behavior across platforms. Third, the HTML specification explicitly defines which roles are valid on each element, and violating these rules means your markup is non-conforming.
The best fix is almost always to use native HTML list elements. The ul, ol, and li elements provide built-in list semantics that all browsers and assistive technologies understand without any extra ARIA attributes. Native elements also handle keyboard interaction and focus management correctly out of the box.
If your design requires a custom component where native list markup isn't feasible — for example, a complex card grid that functions as a list — you can apply role="list" to a semantically neutral element like div or span. Each direct child acting as a list item should then receive role="listitem". This approach satisfies the ARIA specification while avoiding the element-role conflict.
If you actually need a section to group content within a page region and the list is just part of that section, nest a proper list inside the section rather than trying to make the section itself behave as a list.
Examples
❌ Bad: role="list" on a section element
This triggers the validation error because list is not an allowed role for section.
<section role="list">
<div>Item A</div>
<div>Item B</div>
</section>
✅ Fixed: Use native list elements (recommended)
Native lists provide the best accessibility support with no ARIA needed.
<ul>
<li>Item A</li>
<li>Item B</li>
</ul>
✅ Fixed: Nest a list inside the section
If you need the sectioning semantics of section alongside a list, nest the list inside it.
<section aria-labelledby="resources-heading">
<h2 id="resources-heading">Resources</h2>
<ul>
<li>Item A</li>
<li>Item B</li>
</ul>
</section>
✅ Fixed: ARIA roles on a neutral container
Use this only when native list markup is not possible, such as for highly custom UI components.
<div role="list">
<div role="listitem">Item A</div>
<div role="listitem">Item B</div>
</div>
✅ Fixed: Remove the role entirely
If the content isn't actually a list, simply remove the role attribute and let the section carry its natural semantics.
<section>
<h2>Updates</h2>
<div>Item A</div>
<div>Item B</div>
</section>
The article element is a sectioning content element that represents a self-contained composition — a blog post, a news story, a forum entry, or similar independent content. Because it carries the implicit ARIA role of article, it functions as a landmark that assistive technologies recognize and expose to users for navigation. Overriding this landmark role with listitem creates a conflict: the element loses its article semantics for assistive technology users, and the listitem role itself becomes invalid because ARIA's role mapping rules don't permit it on article.
According to the ARIA in HTML specification, each HTML element has a set of allowed ARIA roles. The article element only permits a narrow set of roles (such as application, document, feed, main, none, presentation, and region). The listitem role is not among them. This restriction exists to prevent authors from creating confusing or contradictory semantics that would degrade the accessibility experience.
Beyond validation, this matters for real users. Screen readers rely on role information to convey structure. An article announced as a listitem is misleading — the user loses the ability to navigate by article landmarks, and the listitem may not function correctly without a proper parent list context. Browsers and assistive technologies expect listitem roles to appear as direct children of an element with role="list" (or within native ul/ol elements).
How to fix it
The solution depends on your intent:
- If you want a list of articles, use native HTML list markup (
ulorolwithli) and place eacharticleinside a list item. This gives you both list semantics and article landmarks without any ARIA overrides. - If you can't use native list elements, use neutral
divelements withrole="list"androle="listitem", and nest thearticleinside eachlistitemcontainer. - If the content isn't truly a list, simply remove the
role="listitem"attribute and let thearticleelement use its native semantics.
Examples
Invalid: role="listitem" on an article element
This triggers the validator error because listitem is not an allowed role for article.
<article role="listitem">
<h2>News item</h2>
<p>Details about this story.</p>
</article>
Valid: native list with article elements inside
Using ul and li provides proper list semantics while preserving the article landmark role.
<ul>
<li>
<article>
<h2>News item</h2>
<p>Details about this story.</p>
</article>
</li>
<li>
<article>
<h2>Another item</h2>
<p>More details here.</p>
</article>
</li>
</ul>
Valid: ARIA list roles on neutral containers with nested article elements
When native list elements aren't suitable for your layout, use div elements for the list structure and nest each article inside.
<div role="list">
<div role="listitem">
<article>
<h2>News item</h2>
<p>Details about this story.</p>
</article>
</div>
<div role="listitem">
<article>
<h2>Another item</h2>
<p>More details here.</p>
</article>
</div>
</div>
Valid: standalone article without a list role
If the content doesn't belong in a list, simply remove the invalid role.
<article>
<h2>News item</h2>
<p>Details about this story.</p>
</article>
In every case, the key principle is the same: let the article element keep its native semantics, and use the appropriate list structure around it rather than forcing a conflicting role onto it.
In HTML, every element has a set of ARIA roles it is allowed to carry. The ul element implicitly has the list role, and the ARIA specification only permits certain roles to override it — specifically directory, group, listbox, menu, menubar, none, presentation, radiogroup, tablist, toolbar, and tree. The navigation role is not among them.
The navigation role is a landmark role, meaning it identifies a major section of the page dedicated to navigational links. HTML5 introduced the nav element specifically for this purpose, and it carries the navigation role implicitly — no role attribute needed. When you place role="navigation" on a ul, you're conflicting with the element's semantics. A ul represents a list of items, not a navigational landmark. Assistive technologies like screen readers rely on correct role assignments to help users understand page structure and navigate efficiently. An incorrect role can confuse users by misrepresenting what the element actually is.
Beyond accessibility concerns, this is a standards compliance issue. The W3C validator enforces the rules defined in the ARIA in HTML specification, which maps each HTML element to its allowed roles. Violating these rules means your markup is invalid and may behave unpredictably across different browsers and assistive technologies.
The fix is straightforward: use a nav element as the wrapper for your navigation list. This gives you the navigation landmark semantics automatically, while the ul retains its proper list role. Both elements work together — the nav tells assistive technologies "this is a navigation section," and the ul tells them "here is a list of links."
Examples
❌ Incorrect: navigation role on a ul
This triggers the validation error because navigation is not an allowed role for ul.
<ul role="navigation">
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
✅ Correct: wrapping the ul in a nav element
The nav element provides the navigation landmark implicitly. No role attribute is needed.
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
✅ Correct: labeling multiple navigation landmarks
When a page has more than one nav element, use aria-label to distinguish them for screen reader users.
<nav aria-label="Main">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<nav aria-label="Footer">
<ul>
<li><a href="/privacy">Privacy Policy</a></li>
<li><a href="/terms">Terms of Service</a></li>
</ul>
</nav>
✅ Correct: using an allowed role on ul
If you need the ul to behave as something other than a plain list — for example, a menu in a web application — use one of its permitted roles.
<ul role="menubar">
<li role="menuitem"><a href="/">Home</a></li>
<li role="menuitem"><a href="/about">About</a></li>
<li role="menuitem"><a href="/contact">Contact</a></li>
</ul>
Note that menu and menubar roles are meant for application-style menus with keyboard interaction, not for simple site navigation. For standard website navigation, the nav wrapper approach is almost always the right choice.
The HTML specification defines a specific list of allowed ARIA roles for each element. For the <img> element, role="none" is permitted but role="presentation" is not listed as a valid value. This distinction exists even though the WAI-ARIA 1.1 specification treats none and presentation as synonymous — role="none" was introduced as an alias specifically because the word "presentation" was often misunderstood by authors. The HTML spec adopted none as the canonical value for <img>.
Why this matters
Standards compliance: Using a role value not permitted by the HTML specification for a given element produces a validation error. Keeping your HTML valid ensures predictable behavior across browsers and assistive technologies.
Accessibility: The intended purpose of role="presentation" or role="none" is to tell assistive technologies that an element is purely decorative and carries no semantic meaning. However, for images, the established and most reliable way to achieve this is simply providing an empty alt attribute (alt=""). Screen readers already know to skip images with alt="", so adding a role is usually unnecessary.
Clarity of intent: Using alt="" clearly communicates to both browsers and developers that the image is decorative. If the image actually conveys information, it should have a meaningful alt value and no presentation-related role at all.
How to fix it
- If the image is decorative: Remove the
roleattribute entirely and ensure the image hasalt="". This is the simplest and most widely supported approach. - If you need an explicit ARIA role: Replace
role="presentation"withrole="none"and keepalt="". - If the image conveys meaning: Remove the role and provide a descriptive
altattribute that explains what the image communicates.
Examples
❌ Bad: using role="presentation" on an <img>
<img src="divider.png" alt="" role="presentation">
This triggers the validation error because presentation is not an allowed role value for <img> in the HTML specification.
✅ Fixed: decorative image with empty alt (preferred)
<img src="divider.png" alt="">
The empty alt attribute is sufficient to tell assistive technologies the image is decorative. No role is needed.
✅ Fixed: decorative image with role="none"
<img src="divider.png" alt="" role="none">
If you explicitly need an ARIA role, role="none" is the valid value for <img>. The empty alt should still be included.
✅ Fixed: meaningful image with descriptive alt
<img src="quarterly-sales.png" alt="Bar chart showing quarterly sales increasing from $2M to $5M in 2024">
If the image communicates information, provide a descriptive alt and do not use a presentation or none role — doing so would hide the image's meaning from assistive technology users.
❌ Bad: meaningful image incorrectly hidden
<img src="quarterly-sales.png" alt="Sales chart" role="presentation">
This is both invalid HTML (wrong role value for <img>) and an accessibility problem — the role would attempt to hide a meaningful image from screen readers.
The role="presentation" attribute is not allowed on the video element because it is an interactive media element that conveys meaningful content to users.
The role="presentation" (or its synonym role="none") is used to tell assistive technologies that an element is purely decorative and has no semantic meaning. However, the HTML specification restricts which elements can use this role. Interactive elements like video, button, input, and a (with href) cannot have their semantics removed because doing so would hide important functionality from users who rely on assistive technologies.
A video element provides media controls and content that users need to interact with. Stripping its semantics would make it invisible or confusing to screen reader users. If the video is truly decorative (like a background video), there are better approaches than using role="presentation".
If the video is decorative or used as a background, you can hide it from assistive technologies entirely using aria-hidden="true". If the video has meaningful content, keep its native semantics and provide a proper accessible label instead.
Bad Example
<video role="presentation" autoplay muted loop>
<source src="background.mp4" type="video/mp4">
</video>
Fixed Example — Decorative/Background Video
<video aria-hidden="true" autoplay muted loop>
<source src="background.mp4" type="video/mp4">
</video>
Fixed Example — Meaningful Video
<video controls aria-label="Product demo walkthrough">
<source src="demo.mp4" type="video/mp4">
</video>
This error occurs when you use role="presentational" on an HTML element. While the intent is clear — you want to strip the element's implicit ARIA semantics — the value presentational does not exist in the WAI-ARIA specification. It's a common typo or misconception. The two valid roles for this purpose are presentation and none, which are synonyms of each other.
When you apply role="presentation" or role="none" to an element, you're telling assistive technologies (like screen readers) to ignore the element's implicit semantic meaning. For example, a <table> used purely for layout purposes has no tabular data semantics, so role="presentation" removes the table-related semantics from the accessibility tree. However, the content inside the element remains accessible — only the container's semantics (and in some cases, its required associated descendants) are removed.
This matters for several reasons:
- Standards compliance: Using an invalid role value means the attribute is essentially ignored by browsers and assistive technologies, so the element's semantics are not removed as intended.
- Accessibility: If a screen reader encounters an unknown role, it may fall back to the element's default semantics, leading to a confusing experience. For instance, a layout table without a valid
presentationrole may still be announced as a data table. - Browser consistency: Invalid ARIA values can lead to unpredictable behavior across different browsers and assistive technologies.
To fix this, simply replace presentational with presentation or none.
Examples
❌ Incorrect: using the invalid presentational role
<table role="presentational">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</table>
✅ Correct: using presentation
<table role="presentation">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</table>
✅ Correct: using none (synonym for presentation)
<table role="none">
<tr>
<td>Column 1</td>
<td>Column 2</td>
</tr>
</table>
Other elements where this applies
The presentation / none role can be used on various elements, not just tables. Here's another common example with an image used purely for decoration:
<!-- ❌ Incorrect -->
<img src="divider.png" alt="" role="presentational">
<!-- ✅ Correct -->
<img src="divider.png" alt="" role="presentation">
When to use presentation vs. none
Both presentation and none are functionally identical. The none role was introduced later as a clearer name, since presentation can be confused with visual presentation. For maximum browser support, some developers use both:
<table role="none presentation">
<tr>
<td>Layout content</td>
</tr>
</table>
This fallback pattern ensures that older assistive technologies that don't recognize none will still pick up presentation. However, in most modern environments, either value alone is sufficient.
The rowgroup role represents a group of rows within a tabular structure — similar to what <thead>, <tbody>, or <tfoot> provide in a native HTML <table>. According to the ARIA specification, the rowgroup role should be used on elements that serve as structural containers for rows within a grid, table, or treegrid. A <header> element is not appropriate for this role because it carries its own strong semantic meaning (typically banner or a sectioning header), and overriding it with role="rowgroup" creates a conflict that the W3C validator flags.
This matters for several reasons. First, assistive technologies rely on correct role assignments to convey the structure of a page. When a <header> element is given role="rowgroup", screen readers receive contradictory signals about what the element represents, which can confuse users. Second, the HTML specification restricts which ARIA roles can be applied to certain elements — the <header> element does not allow rowgroup as a valid role override.
To fix this issue, you have two main options:
- Replace the
<header>element with a<div>— since<div>has no implicit ARIA role, it freely acceptsrole="rowgroup". - Use native HTML table elements — replace the custom ARIA table structure with
<table>,<thead>,<tbody>,<tr>,<th>, and<td>, which provide the correct semantics without requiring any ARIA roles.
Examples
❌ Incorrect: role="rowgroup" on a <header> element
<div role="table" aria-label="Quarterly Sales">
<header role="rowgroup">
<div role="row">
<span role="columnheader">Quarter</span>
<span role="columnheader">Revenue</span>
</div>
</header>
<div role="rowgroup">
<div role="row">
<span role="cell">Q1</span>
<span role="cell">$1.2M</span>
</div>
</div>
</div>
The validator reports Bad value "rowgroup" for attribute "role" on element "header" because <header> cannot accept the rowgroup role.
✅ Fix option 1: Use a <div> instead of <header>
<div role="table" aria-label="Quarterly Sales">
<div role="rowgroup">
<div role="row">
<span role="columnheader">Quarter</span>
<span role="columnheader">Revenue</span>
</div>
</div>
<div role="rowgroup">
<div role="row">
<span role="cell">Q1</span>
<span role="cell">$1.2M</span>
</div>
</div>
</div>
A <div> is semantically neutral, so role="rowgroup" is perfectly valid on it.
✅ Fix option 2: Use native HTML table elements
<table>
<caption>Quarterly Sales</caption>
<thead>
<tr>
<th>Quarter</th>
<th>Revenue</th>
</tr>
</thead>
<tbody>
<tr>
<td>Q1</td>
<td>$1.2M</td>
</tr>
</tbody>
</table>
Native table elements like <thead> and <tbody> implicitly carry the rowgroup role, so no ARIA attributes are needed at all. This approach is generally preferred because it provides the best accessibility support out of the box and results in cleaner, more maintainable markup. Only use ARIA table roles when you cannot use native HTML tables (for example, when building a custom grid layout that requires non-table CSS).
The search ARIA role is a landmark role, which means it identifies a large, navigable section of a page — specifically, the region that contains the search functionality. Landmark roles help assistive technologies (like screen readers) quickly identify and jump to major sections of a document. Because landmarks describe sections of a page, they belong on container elements that encompass all the parts of the search interface (the label, the input field, the submit button, etc.), not on a single <input> element.
When you place role="search" on an <input>, the validator rejects it because the search role doesn't match the semantics of an input control. An <input> represents a single interactive widget, not a page region. The valid way to indicate that an input field is for search queries is to use <input type="search">, which gives browsers and assistive technologies the correct semantic meaning for that specific control.
Meanwhile, if you want to mark an entire search form as a search landmark, apply role="search" to the <form> element that wraps the search controls. In modern HTML, you can also use the <search> element, which has the implicit search landmark role without needing any ARIA attribute.
How to fix it
- Remove
role="search"from the<input>element. - Change the input's
typeto"search"— this tells browsers and assistive technologies that the field is for search queries. - Apply
role="search"to the wrapping<form>, or use the HTML<search>element as the container.
Examples
❌ Incorrect: role="search" on an <input>
<form>
<label for="query">Search</label>
<input role="search" id="query" name="q">
<button type="submit">Go</button>
</form>
This triggers the validation error because search is not a valid role for <input>.
✅ Correct: type="search" on the input, role="search" on the form
<form role="search">
<label for="query">Search this site</label>
<input type="search" id="query" name="q">
<button type="submit">Go</button>
</form>
Here, role="search" is correctly placed on the <form> element, creating a search landmark. The <input type="search"> conveys the correct semantics for the input field itself.
✅ Correct: Using the <search> element (modern HTML)
<search>
<form>
<label for="query">Search this site</label>
<input type="search" id="query" name="q">
<button type="submit">Go</button>
</form>
</search>
The <search> element has the implicit ARIA role of search, so no explicit role attribute is needed on either the container or the form. This is the most semantic approach in browsers that support it.
✅ Correct: Standalone search input without a landmark
If you simply need a search-styled input without marking up a full landmark region, just use type="search":
<label for="filter">Filter results</label>
<input type="search" id="filter" name="filter">
This gives the input the correct semantics and allows browsers to provide search-specific UI features (such as a clear button) without requiring a landmark role.
The value section does not exist in the WAI-ARIA specification. ARIA defines a specific set of role values, and section is not among them. This is likely a confusion between the HTML element name <section> and the ARIA role region, which is the role that the <section> element implicitly maps to. Because section is not a recognized role, the validator rejects it as an invalid value.
This matters for several reasons. First, assistive technologies like screen readers rely on ARIA roles to communicate the purpose of elements to users. An unrecognized role value may be ignored entirely or cause unexpected behavior, degrading the experience for users who depend on these tools. Second, the <section> element already carries native semantics equivalent to role="region" (when it has an accessible name), so adding a redundant or incorrect role provides no benefit and introduces potential problems.
According to the ARIA in HTML specification, you should generally avoid setting a role on elements that already have appropriate native semantics. The <section> element's implicit role is region, so explicitly adding role="region" is redundant in most cases. The simplest and best fix is to remove the role attribute altogether and let the native HTML semantics do their job.
If you do need to override an element's role for a specific design pattern (for example, turning a <section> into a navigation landmark), use a valid ARIA role from the WAI-ARIA specification.
Examples
Incorrect: using the invalid section role
<section role="section">
<h2>About Us</h2>
<p>Learn more about our team.</p>
</section>
This triggers the validation error because section is not a valid ARIA role value.
Correct: remove the role attribute
<section>
<h2>About Us</h2>
<p>Learn more about our team.</p>
</section>
The <section> element already provides the correct semantics. No role attribute is needed.
Correct: use a valid ARIA role if needed
If you have a specific reason to assign a role, use a valid one. For example, if a <section> is being used as a navigation landmark:
<section role="navigation" aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</section>
In practice, you would typically use a <nav> element instead, which has the navigation role natively. This example simply illustrates that if you do apply a role, it must be a valid ARIA role value.
Correct: explicit region role with an accessible name
If you want to explicitly mark a section as a named region landmark, you can use role="region" along with an accessible name. However, this is redundant when using <section> with an aria-label or aria-labelledby, since the browser already maps it to region:
<!-- Preferred: native semantics handle the role -->
<section aria-labelledby="features-heading">
<h2 id="features-heading">Features</h2>
<p>Explore our product features.</p>
</section>
<!-- Also valid but redundant -->
<section role="region" aria-labelledby="features-heading">
<h2 id="features-heading">Features</h2>
<p>Explore our product features.</p>
</section>
Both are valid HTML, but the first approach is cleaner and follows the principle of relying on native semantics whenever possible.
The article element has an implicit ARIA role of article, which signals to assistive technologies that it contains a self-contained, independently distributable piece of content—like a blog post, news story, or forum entry. When you add role="tabpanel" to an article, you're attempting to override this strong semantic meaning with a widget role, which the HTML specification does not permit. The ARIA in HTML specification defines a strict set of allowed roles for each HTML element, and tabpanel is not in the list of permissible roles for article.
This matters for several reasons. First, assistive technologies like screen readers rely on accurate role information to communicate the purpose of elements to users. An article element claiming to be a tabpanel creates a confusing and contradictory signal. Second, the W3C validator flags this as an error, meaning your markup is technically invalid. Third, browsers may handle this conflict inconsistently—some might honor the explicit role, while others might prioritize the element's native semantics, leading to unpredictable behavior across platforms.
The tabpanel role is designed for use in a tab interface pattern alongside role="tablist" and role="tab". According to the WAI-ARIA Authoring Practices, a tab panel should be a generic container that holds the content associated with a tab. Elements like div and section are ideal because they don't carry conflicting implicit roles (div has no implicit role, and section maps to region only when given an accessible name, which gets properly overridden by tabpanel).
To fix the issue, simply change the article element to a div or section. If you genuinely need the semantic meaning of article within a tab panel, nest the article inside the div that carries the tabpanel role.
Examples
Incorrect: tabpanel role on an article element
<article role="tabpanel" id="panel1">
<h2>Latest News</h2>
<p>Tab panel content here.</p>
</article>
This triggers the validation error because article does not allow the tabpanel role.
Correct: tabpanel role on a div
<div role="tabpanel" id="panel1">
<h2>Latest News</h2>
<p>Tab panel content here.</p>
</div>
Correct: Nesting an article inside the tab panel
If you need the article semantics for the content within the panel, nest it:
<div role="tabpanel" id="panel1">
<article>
<h2>Latest News</h2>
<p>This is a self-contained article displayed within a tab panel.</p>
</article>
</div>
Full tab interface example
Here's a complete, valid tab interface implementation:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Tab Interface Example</title>
</head>
<body>
<div role="tablist" aria-label="Topics">
<button role="tab" id="tab1" aria-controls="panel1" aria-selected="true">News</button>
<button role="tab" id="tab2" aria-controls="panel2" aria-selected="false">Sports</button>
</div>
<div role="tabpanel" id="panel1" aria-labelledby="tab1">
<p>Latest news content goes here.</p>
</div>
<div role="tabpanel" id="panel2" aria-labelledby="tab2" hidden>
<p>Sports content goes here.</p>
</div>
</body>
</html>
Note the use of aria-controls on each tab to reference its corresponding panel, aria-labelledby on each tabpanel to reference its controlling tab, and the hidden attribute on inactive panels. These associations ensure assistive technologies can properly navigate the tab interface.
The W3C validator raises this error because ARIA roles must be compatible with the element they are applied to. A <ul> element has an implicit ARIA role of list, and overriding it with tabpanel creates a conflict. The tabpanel role signals to assistive technologies that the element is a panel of content activated by a corresponding tab. When this role is placed on a <ul>, screen readers lose the semantic meaning of the list (item count, list navigation, etc.) while also misrepresenting the element's function in the tab interface.
This matters for several reasons:
- Accessibility: Screen reader users rely on correct roles to navigate and understand page structure. A
<ul>marked astabpanelconfuses both its list semantics and its role in the tab interface. - Standards compliance: The ARIA in HTML specification defines which roles are allowed on which elements. The
tabpanelrole is not permitted on<ul>. - Browser behavior: Browsers may handle conflicting roles inconsistently, leading to unpredictable behavior across assistive technologies.
The fix is straightforward: wrap the <ul> inside a proper container element (like a <div> or <section>) and apply the tabpanel role to that container instead.
Examples
Incorrect: tabpanel role on a <ul>
This triggers the validation error because tabpanel is not a valid role for <ul>:
<div role="tablist" aria-label="Recipe categories">
<button role="tab" aria-controls="panel-1" aria-selected="true" id="tab-1">Appetizers</button>
<button role="tab" aria-controls="panel-2" aria-selected="false" id="tab-2">Desserts</button>
</div>
<ul role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<li>Bruschetta</li>
<li>Spring rolls</li>
</ul>
<ul role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
<li>Tiramisu</li>
<li>Cheesecake</li>
</ul>
Correct: tabpanel role on a container wrapping the <ul>
Move the tabpanel role to a <div> and nest the <ul> inside it. This preserves both the tab panel semantics and the list semantics:
<div role="tablist" aria-label="Recipe categories">
<button role="tab" aria-controls="panel-1" aria-selected="true" id="tab-1">Appetizers</button>
<button role="tab" aria-controls="panel-2" aria-selected="false" id="tab-2">Desserts</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<ul>
<li>Bruschetta</li>
<li>Spring rolls</li>
</ul>
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
<ul>
<li>Tiramisu</li>
<li>Cheesecake</li>
</ul>
</div>
Correct: Using <section> as the tab panel
A <section> element also works well as a tab panel container, especially when the panel content is more complex:
<div role="tablist" aria-label="Project info">
<button role="tab" aria-controls="tasks-panel" aria-selected="true" id="tasks-tab">Tasks</button>
<button role="tab" aria-controls="notes-panel" aria-selected="false" id="notes-tab">Notes</button>
</div>
<section role="tabpanel" id="tasks-panel" aria-labelledby="tasks-tab">
<h2>Current tasks</h2>
<ul>
<li>Review pull requests</li>
<li>Update documentation</li>
</ul>
</section>
<section role="tabpanel" id="notes-panel" aria-labelledby="notes-tab" hidden>
<h2>Meeting notes</h2>
<p>Discussed project timeline and milestones.</p>
</section>
In a properly structured tabbed interface:
- The
tablistrole goes on the container that holds the tab buttons. - Each tab trigger gets
role="tab"witharia-controlspointing to its panel'sid. - Each content panel gets
role="tabpanel"on a generic container like<div>or<section>, witharia-labelledbyreferencing the corresponding tab'sid. - List elements like
<ul>and<ol>should remain inside the panel as regular content, retaining their native list semantics.
A span element cannot have role="text" because text is not a valid WAI-ARIA role.
The role attribute accepts only values defined in the WAI-ARIA specification. Common valid roles include button, alert, status, img, presentation, and many others, but text is not among them.
The non-standard role="text" was sometimes used as a workaround to prevent screen readers like VoiceOver from splitting inline elements into separate announcements. For example, when a span wraps mixed text and inline elements, some screen readers read each child element as a separate item. Using role="text" forced them to treat the content as a single text run. However, this was never part of the ARIA spec and causes a validation error.
If the goal is to group inline content so screen readers announce it as one continuous phrase, use role="group" with an aria-label that provides the full text. Alternatively, if the span has no semantic purpose, remove the role attribute entirely.
HTML examples
Invalid usage
<p>
<span role="text">
Sale price: <strong>$49.99</strong>
</span>
</p>
Valid alternatives
Remove the role if it serves no accessibility purpose:
<p>
<span>
Sale price: <strong>$49.99</strong>
</span>
</p>
Or use role="group" with aria-label to ensure screen readers announce the content as a single phrase:
<p>
<span role="group" aria-label="Sale price: $49.99">
Sale price: <strong>$49.99</strong>
</span>
</p>
The <a> element has an implicit ARIA role of link (when it has an href) or generic (when it doesn't). Certain ARIA state attributes, like aria-checked, are only valid on elements with specific roles that support them. For instance, aria-checked is designed for roles like checkbox, menuitemcheckbox, radio, switch, or option. If you place aria-checked on an <a> element without assigning one of these compatible roles, the validator raises this error because the attribute doesn't make sense in the context of the element's current role.
This matters for several reasons. Screen readers and other assistive technologies rely on the relationship between roles and their supported states to convey meaningful information to users. An aria-checked attribute on a plain link creates a confusing experience — the user hears that something is "checked" but the element is announced as a link, which isn't a concept that supports checked/unchecked states. This mismatch can make interfaces unusable for people relying on assistive technology.
To fix this issue, you need to either:
- Add an appropriate
rolethat supports the ARIA state attribute you're using. - Use a more semantically appropriate element, such as
<input type="checkbox">or<button>, which natively supports the concept of being checked or toggled. - Remove the unsupported ARIA attribute if it doesn't actually reflect the element's behavior.
Examples
Incorrect: aria-checked without a compatible role
This triggers the validation error because <a> doesn't support aria-checked without an explicit role:
<a href="#" aria-checked="true">Dark mode</a>
Fixed: Adding a compatible role
Adding role="menuitemcheckbox" (within a menu context) or role="switch" makes aria-checked valid:
<ul role="menu">
<li role="none">
<a href="#" role="menuitemcheckbox" aria-checked="true">Show notifications</a>
</li>
<li role="none">
<a href="#" role="menuitemcheckbox" aria-checked="false">Dark mode</a>
</li>
</ul>
Fixed: Using a <button> with role="switch" instead
In many cases, a <button> is a better semantic fit than an <a> for toggle-like interactions:
<button role="switch" aria-checked="true">Dark mode</button>
Correct: Tab list using <a> elements with proper roles
When building a tab interface with anchor elements, each tab needs role="tab" along with supporting attributes like aria-selected:
<div class="tab-interface">
<div role="tablist" aria-label="Settings">
<a role="tab" href="#panel-1" aria-selected="true" aria-controls="panel-1" id="tab-1">
General
</a>
<a role="tab" href="#panel-2" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
Advanced
</a>
</div>
<div id="panel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
<p>General settings content</p>
</div>
<div id="panel-2" role="tabpanel" tabindex="0" aria-labelledby="tab-2" hidden>
<p>Advanced settings content</p>
</div>
</div>
Incorrect: aria-selected on a plain <a> without a role
<a href="/settings" aria-selected="true">Settings</a>
Fixed: Adding the appropriate role
<a href="/settings" role="tab" aria-selected="true">Settings</a>
When choosing a fix, always consider whether the <a> element is truly the best choice. If the element doesn't navigate the user to a new URL, a <button> is usually more appropriate. Reserve <a> for actual navigation, and use ARIA roles and states only when they accurately describe the element's behavior in the interface.
According to the HTML specification, the <a> element can exist without an href attribute, but in that case it represents a placeholder where a link might otherwise have been placed. However, the validator flags this as an issue because an <a> element without href and without role is ambiguous — browsers won't treat it as a link (it won't be focusable or keyboard-accessible), and assistive technologies won't know how to present it to users.
This matters for several reasons:
- Accessibility: Without
href, the<a>element loses its implicitrole="link"and is no longer announced as a link by screen readers. It also won't appear in the tab order, making it invisible to keyboard users. - Semantics: If the element is styled and scripted to behave like a button or link but lacks the proper attributes, it creates a disconnect between what users see and what the browser understands.
- Standards compliance: The spec expects you to be explicit about the element's purpose when
hrefis absent.
The most common cause of this issue is using <a> elements as JavaScript-only click targets without providing an href, or using them as styled containers without any interactive purpose.
How to Fix It
There are several approaches depending on your intent:
- Add an
hrefattribute if the element should be a link. This is the most common and recommended fix. - Add a
roleattribute if you're deliberately using<a>withouthreffor a specific purpose, such asrole="button". - Use a different element entirely. If it's not a link, consider using a
<button>,<span>, or another semantically appropriate element.
Examples
❌ Missing both href and role
<a>Click here</a>
This triggers the validator error because the <a> has neither href nor role.
❌ JavaScript-only handler without href
<a onclick="doSomething()">Submit</a>
Even with an event handler, the element lacks href and role, so it fails validation and is inaccessible to keyboard users.
✅ Fix by adding an href
<a href="/about">About us</a>
Adding href makes it a proper hyperlink — focusable, keyboard-accessible, and recognized by screen readers.
✅ Fix by adding role for a non-link purpose
<a role="button" tabindex="0" onclick="doSomething()">Submit</a>
If you must use <a> without href as a button, add role="button" and tabindex="0" to ensure it's focusable and properly announced. However, consider using a real <button> instead.
✅ Better: use the right element
<button type="button" onclick="doSomething()">Submit</button>
If the element triggers an action rather than navigating somewhere, a <button> is the correct semantic choice. It's focusable by default, responds to keyboard events, and doesn't need extra attributes.
✅ Placeholder anchor (intentional non-link)
<a role="link" aria-disabled="true">Coming soon</a>
If you're intentionally showing a placeholder where a link will eventually appear, you can add a role and indicate its disabled state for assistive technologies. Alternatively, use a <span> with appropriate styling to avoid the issue altogether.
The HTML specification defines <button> as a versatile interactive element, but its behavior changes depending on context. When a <button> is placed inside a <form> without a type attribute, it defaults to type="submit", which can cause unexpected form submissions. The validator flags this because relying on the implicit default is ambiguous and error-prone. Explicitly setting the type attribute makes the button's intent clear to both developers and browsers.
The three valid values for the type attribute are:
submit— submits the parent form's data to the server.reset— resets all form controls to their initial values.button— performs no default action; behavior is defined via JavaScript.
When a <button> is given an ARIA role of checkbox, switch, or menuitemcheckbox, the validator expects an aria-checked attribute to accompany it. These roles describe toggle controls that have a checked or unchecked state, so assistive technologies need to know the current state. Without aria-checked, screen readers cannot communicate whether the control is on or off, making the interface inaccessible.
The aria-checked attribute accepts the following values:
true— the control is checked or on.false— the control is unchecked or off.mixed— the control is in an indeterminate state (valid forcheckboxandmenuitemcheckboxroles only).
How to fix it
For standard buttons, add the type attribute with the appropriate value. If the button triggers JavaScript behavior and is not meant to submit a form, use type="button". If it submits a form, use type="submit" explicitly to make the intent clear.
For toggle buttons, ensure the <button> has both a role attribute (such as checkbox or switch) and an aria-checked attribute that reflects the current state. You should also include type="button" to prevent unintended form submission. Use JavaScript to toggle the aria-checked value when the user interacts with the button.
Examples
Missing type attribute
This triggers the validator warning because the type is not specified:
<form action="/search">
<input type="text" name="q">
<button>Search</button>
</form>
Fixed by adding an explicit type:
<form action="/search">
<input type="text" name="q">
<button type="submit">Search</button>
</form>
Button used outside a form without type
<button onclick="openMenu()">Menu</button>
Fixed by specifying type="button":
<button type="button" onclick="openMenu()">Menu</button>
Toggle button missing aria-checked
A button with role="switch" but no aria-checked attribute:
<button type="button" role="switch">Dark Mode</button>
Fixed by adding aria-checked:
<button type="button" role="switch" aria-checked="false">Dark Mode</button>
Checkbox-style toggle button
A button acting as a checkbox must include both role="checkbox" and aria-checked:
<button type="button" role="checkbox" aria-checked="false">
Enable notifications
</button>
Complete toggle example with all required attributes
<button type="button" role="switch" aria-checked="false" id="wifi-toggle">
Wi-Fi
</button>
<script>
document.getElementById("wifi-toggle").addEventListener("click", function () {
const isChecked = this.getAttribute("aria-checked") === "true";
this.setAttribute("aria-checked", String(!isChecked));
});
</script>
In this example, the type="button" prevents form submission, the role="switch" tells assistive technologies this is a toggle, and aria-checked is updated dynamically to reflect the current state. This ensures the button is fully accessible and passes validation.
The aria-expanded attribute cannot be used on a plain div element without also specifying a role attribute.
The aria-expanded attribute indicates whether a grouping of content that the element owns or controls is currently expanded or collapsed. However, this attribute is only valid on elements that have an appropriate implicit or explicit role. A plain div has no implicit ARIA role, so you must assign one explicitly.
The aria-expanded attribute is commonly used with interactive roles such as button, combobox, treeitem, or link. Adding the correct role tells assistive technologies what kind of element the user is interacting with, making aria-expanded meaningful in context.
HTML Examples
❌ Invalid: aria-expanded on a plain div
<div aria-expanded="false">
Menu content
</div>
✅ Valid: aria-expanded with an appropriate role
<div role="button" aria-expanded="false">
Menu content
</div>
Alternatively, consider using a native HTML element that already carries the correct semantics, which avoids the need for a role attribute entirely:
<button aria-expanded="false">
Toggle Menu
</button>
Using a native <button> is generally preferred over <div role="button"> because it comes with built-in keyboard interaction and focus behavior.
The aria-required attribute tells assistive technologies that a form field must be filled in before the form can be submitted. However, this attribute is only valid on elements that function as interactive widgets. A bare div has no implicit ARIA role, so assistive technologies have no context for what kind of input is expected. The validator flags this because an aria-required attribute on a generic div is effectively meaningless without additional ARIA attributes that define the element's role and behavior.
This matters for several reasons:
- Accessibility: Screen readers and other assistive technologies rely on roles to understand how to present an element to users. Without a role,
aria-requiredprovides incomplete information. - Standards compliance: The WAI-ARIA specification defines which attributes are allowed on which roles. Using
aria-requiredwithout an established role violates these constraints. - Browser behavior: Browsers may ignore or misinterpret ARIA attributes when they appear on elements that lack the proper role context.
How to fix it
Option 1: Use native semantic HTML (preferred)
Whenever possible, use native HTML form elements. They come with built-in accessibility semantics, keyboard interaction, and validation — no ARIA needed.
Replace a div with aria-required="true" with an appropriate form control using the native required attribute:
<input type="text" required>
<select required>
<option value="">Choose one</option>
<option value="1">Option 1</option>
</select>
<textarea required></textarea>
Option 2: Add an appropriate role attribute
When you must use a div as a custom widget (styled and enhanced with CSS and JavaScript), add the correct role attribute to give it semantic meaning. Choose the role that matches the widget's actual behavior — don't just pick one arbitrarily.
Common roles that support aria-required:
combobox— a custom dropdown with text inputlistbox— a custom selection listradiogroup— a group of radio-like optionsspinbutton— a numeric stepper (also requiresaria-valuemax,aria-valuemin, andaria-valuenow)textbox— a custom text input
Option 3: Add other qualifying ARIA attributes
For certain widgets like sliders or spinbuttons, you may need aria-valuemax, aria-valuemin, and aria-valuenow in addition to (or as part of) defining the role. These attributes inherently establish a widget context.
Examples
❌ Invalid: aria-required on a plain div
<div aria-required="true">
<div data-value="One">1</div>
<div data-value="Two">2</div>
<div data-value="Three">3</div>
</div>
This triggers the validation error because the div has no role or other ARIA attributes to define what kind of widget it is.
✅ Fixed: Adding the appropriate role
<div aria-required="true" role="radiogroup" aria-label="Pick a number">
<div role="radio" aria-checked="false" tabindex="0">1</div>
<div role="radio" aria-checked="false" tabindex="0">2</div>
<div role="radio" aria-checked="false" tabindex="0">3</div>
</div>
Adding role="radiogroup" gives the div a semantic identity. Note that child elements also need appropriate roles and attributes for the widget to be fully accessible.
✅ Fixed: Using native HTML instead
<fieldset>
<legend>Pick a number</legend>
<label><input type="radio" name="number" value="1" required> 1</label>
<label><input type="radio" name="number" value="2"> 2</label>
<label><input type="radio" name="number" value="3"> 3</label>
</fieldset>
This approach uses native radio buttons with the required attribute, eliminating the need for ARIA entirely. The browser handles accessibility, keyboard navigation, and form validation automatically.
✅ Fixed: A custom spinbutton with value attributes
<div role="spinbutton"
aria-required="true"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="50"
aria-label="Quantity"
tabindex="0">
50
</div>
For a spinbutton role, you must also provide aria-valuemin, aria-valuemax, and aria-valuenow to fully describe the widget's state.
The aria-expanded attribute requires the element to have an appropriate role attribute (or be an element that natively implies one). A <span> is a generic inline element with no implicit ARIA role, so you must explicitly assign a role when using ARIA state attributes like aria-expanded.
The aria-expanded attribute indicates whether a grouping element controlled by this element is currently expanded or collapsed. It is only valid on elements with specific roles such as button, link, combobox, menuitem, or other widget roles. When a <span> uses aria-expanded without a role, validators flag it because there's no semantic context for that state.
Since this element toggles a dropdown menu and has aria-label, aria-controls, and aria-expanded, the most appropriate role is button. This tells assistive technologies that the element is interactive and can be activated.
Also note that when using role="button" on a non-interactive element like <span>, you should ensure it is focusable by adding tabindex="0" and that it handles keyboard events (Enter and Space keys).
HTML Examples
❌ Invalid: aria-expanded on a span without a role
<span class="navbar-dropdown-icon"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</span>
✅ Valid: adding role="button" and tabindex="0"
<span class="navbar-dropdown-icon"
role="button"
tabindex="0"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</span>
✅ Better: use a <button> element instead
<button class="navbar-dropdown-icon"
type="button"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</button>
Using a native <button> is preferred because it is focusable and keyboard-accessible by default, without needing role or tabindex.
The span element is a generic inline container with no inherent semantics. On its own, it carries no meaning for assistive technologies. When you add ARIA attributes like aria-expanded or aria-valuenow to a span, you are signaling that the element represents an interactive widget — but the validator (and assistive technologies) need more context. Many ARIA attributes are only permitted on elements that have certain roles, and some roles require a specific set of attributes to function correctly.
For example, aria-valuenow is designed for range widgets like sliders and progress bars. According to the WAI-ARIA specification, if you use aria-valuenow, the element must also have aria-valuemin, aria-valuemax, and a role such as progressbar, slider, meter, or scrollbar. Similarly, aria-expanded is meant for elements with roles like button, combobox, link, or treeitem. Placing these attributes on a bare span without the corresponding role violates the ARIA rules and triggers this validation error.
This matters for several reasons:
- Accessibility: Screen readers rely on the
roleto determine how to present a widget to users. Without it, ARIA state attributes become meaningless or confusing. - Standards compliance: The HTML specification integrates ARIA rules, and validators enforce that ARIA attributes are used in valid combinations.
- Browser behavior: Browsers use the
roleto build the accessibility tree. Aspanwitharia-valuenowbut norolemay be ignored or misrepresented to assistive technology users.
How to fix it
- Add the correct
roleto thespan, along with all attributes required by that role. - Use a semantic HTML element instead of a
spanwhen one exists (e.g.,<progress>or<button>). - Remove unnecessary ARIA attributes if the
spanis purely decorative or the attributes were added by mistake.
If your span is purely visual (e.g., a decorative asterisk for required fields), don't add state-related ARIA attributes to it. Instead, use aria-hidden="true" to hide it from assistive technologies, and place ARIA attributes on the actual form control.
Examples
Incorrect: aria-expanded on a span without a role
<span aria-expanded="false">Menu</span>
The validator reports the missing role because aria-expanded isn't valid on a generic span.
Correct: Add a role (or use a button)
<span role="button" tabindex="0" aria-expanded="false">Menu</span>
Or, better yet, use a real button element:
<button aria-expanded="false">Menu</button>
Incorrect: aria-valuenow without the full set of range attributes and role
<span class="progress-indicator" aria-valuenow="50">50%</span>
Correct: Include the role and all required range attributes
<span role="progressbar" aria-valuenow="50" aria-valuemin="0" aria-valuemax="100">
50%
</span>
Or use the native <progress> element, which has built-in semantics:
<progress value="50" max="100">50%</progress>
Incorrect: aria-required on a decorative span
<label for="email">
<span class="required" aria-required="true">*</span>
</label>
<input id="email" name="email" type="email">
The aria-required attribute belongs on the form control, not on the decorative asterisk.
Correct: Hide the decorative indicator and mark the input as required
<label for="email">
<span class="required" aria-hidden="true">*</span>
</label>
<input id="email" name="email" type="email" aria-required="true">
If you also want screen readers to announce "required" as part of the label text, add visually hidden text:
<label for="email">
<span aria-hidden="true">*</span>
<span class="visually-hidden">required</span>
</label>
<input id="email" name="email" type="email" required>
The key takeaway: whenever you use ARIA state or property attributes on a span, make sure the element also has the correct role and all companion attributes required by that role. When a native HTML element already provides the semantics you need — such as <button>, <progress>, or <meter> — prefer it over a span with ARIA, as native elements are more robust and require less additional markup.
The summary element needs an explicit role attribute when the W3C validator detects it's being used in a context where its implicit ARIA semantics are unclear or overridden.
The summary element is designed to be used as the first child of a <details> element, where it acts as a clickable disclosure toggle. When used correctly inside <details>, it has an implicit ARIA role and doesn't need additional attributes.
This validation warning typically appears when:
- The
summaryelement is used outside of a<details>element. - The
summaryelement has an explicitroleattribute that requires additional ARIA properties (e.g.,role="checkbox"requiresaria-checked, orrole="heading"requiresaria-level).
The simplest fix is to ensure summary is used correctly as a direct child of <details>, and to remove any unnecessary or conflicting role attributes.
Example with the issue
<!-- summary outside of details triggers the warning -->
<summary>Click to expand</summary>
<p>Some content here.</p>
<!-- Or summary with an incomplete role override -->
<details>
<summary role="heading">Section Title</summary>
<p>Some content here.</p>
</details>
How to fix it
<!-- Use summary correctly inside details -->
<details>
<summary>Click to expand</summary>
<p>Some content here.</p>
</details>
<!-- If you need a heading role, include the required aria-level -->
<details>
<summary role="heading" aria-level="3">Section Title</summary>
<p>Some content here.</p>
</details>
If you don't have a specific reason to override the role, simply remove the role attribute and let the summary element keep its native semantics within <details>.
The aria-checked attribute communicates the checked state of an interactive widget to assistive technologies. According to the WAI-ARIA specification, this attribute is only permitted on elements that have a role supporting the "checked" state — such as checkbox, switch, radio, menuitemcheckbox, or menuitemradio. A plain <td> element has an implicit role of cell (or gridcell when inside a role="grid" table), neither of which supports aria-checked. When the validator encounters aria-checked on a <td> without a compatible role, it flags the element as invalid.
This matters for several reasons:
- Accessibility: Screen readers and other assistive technologies rely on the relationship between
roleand ARIA state attributes. Anaria-checkedon an element without a recognized checkable role creates a confusing or broken experience — users may not understand that the cell is supposed to be interactive. - Standards compliance: The ARIA in HTML specification defines strict rules about which attributes are allowed on which roles. Violating these rules means your HTML is technically invalid.
- Browser behavior: Browsers may ignore
aria-checkedentirely when it's used on an element without a valid role, making the attribute useless.
How to fix it
You have two main approaches depending on what your <td> is meant to do:
1. Add an appropriate role attribute. If the table cell genuinely represents a checkable control (for example, in an interactive data grid), add role="checkbox", role="switch", or another appropriate checkable role to the <td>, along with tabindex for keyboard accessibility.
2. Remove aria-checked and use a real control. If the cell simply contains a checkbox or toggle, place an actual <input type="checkbox"> inside the <td> and remove the ARIA attributes from the cell itself. Native HTML controls already communicate their state to assistive technologies without extra ARIA.
Examples
❌ Incorrect: aria-checked without a role
<table>
<tr>
<td aria-checked="true">Selected</td>
<td>Item A</td>
</tr>
</table>
This triggers the error because <td> has the implicit role of cell, which does not support aria-checked.
✅ Fix: Add a compatible role to the <td>
<table role="grid">
<tr>
<td role="checkbox" aria-checked="true" tabindex="0">Selected</td>
<td>Item A</td>
</tr>
</table>
Here the <td> explicitly has role="checkbox", which supports aria-checked. The tabindex="0" makes it keyboard-focusable, and role="grid" on the table signals that cells may be interactive.
✅ Fix: Use a native checkbox inside the <td>
<table>
<tr>
<td>
<label>
<input type="checkbox" checked>
Selected
</label>
</td>
<td>Item A</td>
</tr>
</table>
This approach is often the best option. The native <input type="checkbox"> already conveys its checked state to assistive technologies, and no ARIA attributes are needed on the <td>.
❌ Incorrect: Mismatched role and aria-checked
<table>
<tr>
<td role="button" aria-checked="false">Toggle</td>
<td>Item B</td>
</tr>
</table>
The button role does not support aria-checked. This would trigger a different but related validation error.
✅ Fix: Use a role that supports aria-checked
<table role="grid">
<tr>
<td role="switch" aria-checked="false" tabindex="0">Toggle</td>
<td>Item B</td>
</tr>
</table>
The switch role supports aria-checked and is appropriate for toggle-style controls.
ARIA (Accessible Rich Internet Applications) works as a system where roles define what an element is, and states and properties describe the element's current condition or characteristics. Certain ARIA attributes are only valid when used on elements that have a specific role — either explicitly declared via the role attribute or implicitly provided by the HTML element itself. When you add an ARIA state or property to a generic element like a <div> or <span> without specifying a role, assistive technologies have no context for interpreting that attribute. For example, aria-expanded="true" on a plain <div> tells a screen reader that something is expanded, but it doesn't communicate what is expanded — is it a button, a navigation menu, a tree item? The role provides that crucial context.
This matters for several reasons:
- Accessibility: Screen readers and other assistive technologies rely on the combination of roles and their associated states/properties to convey meaningful information to users. An ARIA property without a role is ambiguous and can lead to a confusing experience.
- Standards compliance: The WAI-ARIA specification defines which states and properties are allowed on which roles. Using an ARIA attribute outside of a valid role context violates the spec.
- Predictable behavior: Browsers and assistive technologies may handle orphaned ARIA attributes inconsistently, leading to unpredictable results across different platforms.
To fix this issue, you have two approaches:
- Add an explicit
roleattribute to the element, choosing a role that supports the ARIA attributes you're using. - Use a semantic HTML element that already has an implicit ARIA role. For instance,
<nav>has an implicit role ofnavigation,<button>has an implicit role ofbutton, and<header>has an implicit role ofbanner. This is generally the preferred approach, as it provides built-in keyboard interaction and semantics without extra effort.
When choosing a role, make sure the ARIA states and properties you're using are actually supported by that role. For example, aria-expanded is supported by roles like button, combobox, link, treeitem, and others — but not by every role. Consult the WAI-ARIA roles documentation to verify compatibility.
Examples
Invalid: ARIA property without a role
This <div> uses aria-expanded but has no role, so the validator doesn't know what kind of element this is supposed to be.
<div aria-expanded="true">
Menu contents
</div>
Fixed: Adding an explicit role
Adding role="button" tells assistive technologies that this is a button that can be expanded or collapsed.
<div role="button" aria-expanded="true" tabindex="0">
Menu contents
</div>
Fixed: Using a semantic HTML element instead
A <button> element already has an implicit button role, so no explicit role attribute is needed. This is the preferred approach.
<button aria-expanded="true">
Toggle menu
</button>
Invalid: aria-label on a generic element
A <span> has no implicit role, so aria-label has no meaningful context here.
<span aria-label="Close dialog">X</span>
Fixed: Using a semantic element or adding a role
<button aria-label="Close dialog">X</button>
Or, if you need to use a <span>:
<span role="button" tabindex="0" aria-label="Close dialog">X</span>
Using elements with implicit roles
Many HTML elements already carry implicit ARIA roles, so adding ARIA states and properties to them is valid without an explicit role attribute:
<!-- <nav> has implicit role="navigation" -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
<!-- <details> supports aria-expanded implicitly -->
<details aria-describedby="help-text">
<summary>More information</summary>
<p id="help-text">Additional details about this topic.</p>
</details>
As a general rule, always prefer native semantic HTML elements over generic elements with ARIA roles. Native elements come with built-in keyboard support, focus management, and accessibility semantics — reducing the amount of custom code you need to write and maintain.
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries