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 role="button" attribute tells assistive technologies like screen readers that an element behaves as a button — a widget used to perform actions such as submitting a form, opening a dialog, or triggering a command. When a <button> element appears inside an element with role="button", the result is a nested interactive control. The HTML specification explicitly forbids this because interactive content must not be nested within other interactive content.
This nesting causes real problems. Screen readers may announce the outer element as a button but fail to recognize or reach the inner <button>. Keyboard users may not be able to focus on or activate the inner control. Different browsers handle the situation inconsistently — some may ignore one of the controls entirely, others may fire events on the wrong element. The end result is an interface that is broken for many users.
This issue commonly arises in a few scenarios:
- A <div> or <span> is given role="button" and then a <button> is placed inside it for styling or click-handling purposes.
- A component library wraps content in a role="button" container, and a developer adds a <button> inside without realizing the conflict.
- A custom card or list item is made clickable with role="button", but also contains action buttons within it.
The fix depends on your intent. If the outer element is the intended interactive control, remove the inner <button> and handle interactions on the outer element. If the inner <button> is the intended control, remove role="button" from the ancestor. If both need to be independently clickable, restructure the markup so neither is a descendant of the other.
Examples
❌ Incorrect: <button> inside an element with role="button"
<div role="button" tabindex="0" onclick="handleClick()">
<button type="button">Click me</button>
</div>
This is invalid because the <button> is a descendant of the <div> that has role="button".
✅ Fix option 1: Use only the <button> element
If the inner <button> is the actual control, remove role="button" from the wrapper:
<div>
<button type="button" onclick="handleClick()">Click me</button>
</div>
✅ Fix option 2: Use only the outer role="button" element
If the outer element is the intended interactive control, remove the inner <button>:
<div role="button" tabindex="0" onclick="handleClick()">
Click me
</div>
Note that when using role="button" on a non-<button> element, you must also handle keyboard events (Enter and Space) manually. A native <button> provides this for free, so prefer option 1 when possible.
❌ Incorrect: Clickable card containing action buttons
<div role="button" tabindex="0" class="card">
<h3>Item title</h3>
<p>Description text</p>
<button type="button">Delete</button>
</div>
✅ Fix: Separate the card link from the action buttons
<div class="card">
<h3><button type="button" class="card-link">Item title</button></h3>
<p>Description text</p>
<button type="button">Delete</button>
</div>
In this approach, the card’s main action is handled by a <button> on the title, while the “Delete” button remains an independent control. Neither is nested inside the other, and both are accessible to keyboard and screen reader users.
Many HTML elements come with built-in (implicit) ARIA roles that browsers and assistive technologies already recognize. The <form> element natively maps to the form ARIA role, meaning screen readers and other tools already understand it as a form landmark without any extra attributes. When you explicitly add role="form" to a <form> element, you’re telling the browser something it already knows.
This redundancy is problematic for several reasons:
- Code clarity: Unnecessary attributes make your HTML harder to read and maintain. Other developers may wonder if the explicit role is there to override something or if it serves a special purpose.
- Misleading intent: Explicit ARIA roles are typically reserved for cases where you need to override or supplement the default semantics of an element. Using them unnecessarily can signal to future maintainers that something unusual is happening when it isn’t.
- ARIA best practices: The first rule of ARIA is “do not use ARIA if you can use a native HTML element or attribute with the semantics and behavior you require.” Adding redundant ARIA roles goes against this principle.
It’s worth noting that the <form> element’s implicit form role only exposes it as a landmark when the form has an accessible name (e.g., via aria-label or aria-labelledby). If you need your form to appear as a landmark region, provide an accessible name rather than adding a redundant role.
To fix this issue, simply remove role="form" from any <form> element. If you want the form to function as a named landmark for assistive technology users, add an accessible name instead.
Examples
❌ Incorrect: redundant role="form"
<form role="form" action="/subscribe" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
This triggers the validator warning because role="form" duplicates the element’s implicit role.
✅ Correct: no explicit role
<form action="/subscribe" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
The <form> element already communicates its role natively. No ARIA attribute is needed.
✅ Correct: form with an accessible name for landmark navigation
<form action="/subscribe" method="post" aria-label="Newsletter subscription">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Subscribe</button>
</form>
If you want the form to be discoverable as a named landmark by screen reader users, provide an aria-label or aria-labelledby attribute — not a redundant role.
Other elements with implicit roles
The same principle applies to many other HTML elements. Avoid adding redundant roles like these:
<!-- ❌ Redundant roles -->
<nav role="navigation">...</nav>
<main role="main">...</main>
<header role="banner">...</header>
<footer role="contentinfo">...</footer>
<button role="button">Click me</button>
<!-- ✅ Let native semantics do the work -->
<nav>...</nav>
<main>...</main>
<header>...</header>
<footer>...</footer>
<button>Click me</button>
Trust the native semantics of HTML elements. Only use explicit ARIA roles when you genuinely need to change or supplement an element’s default behavior.
Many HTML elements come with built-in ARIA roles that assistive technologies already recognize. The <fieldset> element is one of these — its implicit role is group, which tells screen readers that the contained form controls are related. When you add role="group" to a <fieldset>, you’re telling the browser something it already knows.
This redundancy matters for a few reasons:
- Code cleanliness: Unnecessary attributes add clutter, making your markup harder to read and maintain.
- ARIA best practices: The first rule of ARIA is “If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.” Adding role="group" to <fieldset> violates this principle in spirit — it suggests the developer may not understand the element’s native semantics.
- Potential confusion: Explicitly setting roles that match the default can mislead other developers into thinking the role is doing something special, or that removing it would change behavior.
This same principle applies to other elements with implicit roles, such as role="navigation" on <nav>, role="banner" on <header>, or role="button" on <button>. If the element already carries the semantic meaning natively, there’s no need to duplicate it with an explicit ARIA role.
To fix this, simply remove the role="group" attribute from the <fieldset> element. No replacement is needed — the browser and assistive technologies will continue to treat the <fieldset> as a group automatically.
Examples
Incorrect: redundant role="group" on <fieldset>
<form>
<fieldset role="group">
<legend>Shipping Address</legend>
<label for="street">Street:</label>
<input type="text" id="street" name="street">
<label for="city">City:</label>
<input type="text" id="city" name="city">
</fieldset>
</form>
The validator will report that the group role is unnecessary for the <fieldset> element.
Correct: <fieldset> without explicit role
<form>
<fieldset>
<legend>Shipping Address</legend>
<label for="street">Street:</label>
<input type="text" id="street" name="street">
<label for="city">City:</label>
<input type="text" id="city" name="city">
</fieldset>
</form>
The <fieldset> element inherently communicates the group role to assistive technologies, so no ARIA attribute is needed.
When role on <fieldset> is appropriate
There are cases where you might legitimately set a different role on a <fieldset> — for example, role="radiogroup" when the fieldset contains a set of related radio buttons and you want to convey more specific semantics:
<form>
<fieldset role="radiogroup" aria-labelledby="color-legend">
<legend id="color-legend">Favorite Color</legend>
<label><input type="radio" name="color" value="red"> Red</label>
<label><input type="radio" name="color" value="blue"> Blue</label>
<label><input type="radio" name="color" value="green"> Green</label>
</fieldset>
</form>
This is valid because radiogroup is a different role that provides more specific meaning than the default group. The validator only warns when the explicit role matches the element’s implicit role.
HTML heading elements (<h1> through <h6>) have built-in semantic meaning that browsers and assistive technologies already understand. According to the WAI-ARIA specification, each of these elements carries an implicit heading role with a corresponding aria-level — <h1> has aria-level="1", <h2> has aria-level="2", and so on. When you explicitly add role="heading" to one of these elements, you’re telling the browser something it already knows, which clutters your markup without providing any benefit.
This pattern is part of a broader principle in ARIA authoring known as the first rule of ARIA: don’t use ARIA when a native HTML element already provides the semantics you need. Redundant ARIA roles can cause confusion for developers maintaining the code, as it suggests that the role might be necessary or that the element might not otherwise be recognized as a heading. In some edge cases, adding an explicit aria-level that doesn’t match the heading level (e.g., aria-level="3" on an <h1>) can create conflicting information for screen readers, leading to an inconsistent experience for users of assistive technologies.
The role="heading" attribute is designed for situations where you need to give heading semantics to a non-heading element, such as a <div> or <span>. In those cases, you must also include the aria-level attribute to specify the heading’s level. However, whenever possible, using native heading elements is always preferred over this ARIA-based approach.
How to fix it
- Remove role="heading" from any <h1> through <h6> element.
- Remove aria-level if it was added alongside the redundant role and matches the heading’s native level.
- If you genuinely need a non-standard element to act as a heading, use role="heading" with aria-level on that element instead — but prefer native heading elements whenever possible.
Examples
❌ Redundant role on a native heading
<h1 role="heading" aria-level="1">Welcome to My Site</h1>
<h2 role="heading">About Us</h2>
<h3 role="heading" aria-level="3">Our Mission</h3>
All three headings will trigger the validator warning. The role="heading" and aria-level attributes are completely unnecessary here because the elements already convey this information natively.
✅ Native headings without redundant roles
<h1>Welcome to My Site</h1>
<h2>About Us</h2>
<h3>Our Mission</h3>
Simply removing the redundant attributes resolves the issue while preserving full accessibility.
✅ Correct use of the heading role on a non-heading element
In rare cases where you cannot use a native heading element, the heading role is appropriate on a generic element:
<div role="heading" aria-level="2">Section Title</div>
This tells assistive technologies to treat the <div> as a level-2 heading. Note that aria-level is required here since a <div> has no implicit heading level. That said, using a native <h2> is always the better choice:
<h2>Section Title</h2>
❌ Conflicting aria-level on a native heading
Be especially careful with this anti-pattern, where the explicit level contradicts the element:
<h1 role="heading" aria-level="3">Page Title</h1>
This sends mixed signals — the element is an <h1> but claims to be level 3. Screen readers may behave unpredictably. If you need a level-3 heading, use <h3>:
<h3>Page Title</h3>
Every HTML element has an implicit ARIA role defined by the HTML specification. The <img> element’s implicit role is img, which means assistive technologies like screen readers already recognize it as an image without any additional ARIA attributes. Adding role="img" explicitly doesn’t change behavior — it just adds unnecessary noise to your markup and signals that the author may not understand how native semantics work.
The W3C validator flags this because it violates the first rule of ARIA: don’t use ARIA if you can use a native HTML element or attribute that already has the semantics you need. Redundant roles clutter your code, make maintenance harder, and can confuse other developers into thinking the role is there for a specific reason.
The role="img" attribute is genuinely useful in other contexts — for example, when you want to group multiple elements together and have them treated as a single image by assistive technologies. A <div> or <span> has no implicit img role, so adding role="img" to a container is meaningful and appropriate.
How to fix it
Simply remove the role="img" attribute from any <img> element. The image semantics are already built in. Make sure you still provide a meaningful alt attribute for accessibility.
Examples
❌ Redundant role on <img>
<img src="photo.jpg" alt="A sunset over the ocean" role="img">
The validator will warn: The “img” role is unnecessary for element “img”.
✅ Fixed: Remove the redundant role
<img src="photo.jpg" alt="A sunset over the ocean">
No explicit role is needed. The browser already communicates this element as an image.
✅ Legitimate use of role="img" on a non-image element
The role="img" attribute is appropriate when applied to a container that groups multiple elements into a single conceptual image:
<div role="img" aria-label="Star rating: 4 out of 5">
<span>⭐</span>
<span>⭐</span>
<span>⭐</span>
<span>⭐</span>
<span>☆</span>
</div>
Here, the <div> has no inherent image semantics, so role="img" is meaningful — it tells assistive technologies to treat the entire group as a single image described by the aria-label.
✅ Another legitimate use: CSS background image with role="img"
<div role="img" aria-label="Company logo" class="logo-background"></div>
Since a <div> styled with a CSS background image has no image semantics, role="img" paired with aria-label ensures the visual content is accessible.
The <a> element with an href attribute is one of HTML’s most fundamental interactive elements. Browsers and assistive technologies inherently recognize it as a link — it’s focusable via the Tab key, activatable with Enter, and announced as “link” by screen readers. This built-in behavior is part of the element’s implicit ARIA role, which is link.
When you explicitly add role="link" to an <a href="..."> element, you’re telling assistive technologies something they already know. The W3C validator flags this as unnecessary because it violates the principle of not redundantly setting ARIA roles that match an element’s native semantics. This principle is codified in the first rule of ARIA use: “If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.”
While a redundant role="link" won’t typically break anything, it creates noise in your markup. It can also signal to other developers that the role is necessary, leading to confusion or cargo-cult patterns. Clean, semantic HTML that relies on native roles is easier to maintain and less error-prone.
The role="link" attribute is legitimately useful when a non-interactive element like a <span> or <div> needs to behave as a link. In that case, you must also manually implement keyboard interaction (focus via tabindex, activation via Enter key handling) and provide an accessible name. But when you already have a proper <a> element with href, all of that comes for free — no ARIA needed.
Examples
❌ Incorrect: redundant role="link" on an anchor
<a href="/about" role="link">About Us</a>
The role="link" is redundant here because the <a> element with href already has an implicit role of link.
✅ Correct: anchor without redundant role
<a href="/about">About Us</a>
Simply remove the role="link" attribute. The browser and assistive technologies already treat this as a link.
✅ Correct: using role="link" on a non-semantic element (when necessary)
<span role="link" tabindex="0" onclick="location.href='/about'" onkeydown="if(event.key==='Enter') location.href='/about'">
About Us
</span>
This is the legitimate use case for role="link" — when you cannot use a native <a> element and need to make a non-interactive element behave like a link. Note the additional work required: tabindex="0" for keyboard focusability, a click handler, and a keydown handler for Enter key activation. Using a proper <a> element avoids all of this extra effort.
❌ Incorrect: multiple anchors with redundant roles
<nav>
<a href="/" role="link">Home</a>
<a href="/products" role="link">Products</a>
<a href="/contact" role="link">Contact</a>
</nav>
✅ Correct: clean navigation without redundant roles
<nav>
<a href="/">Home</a>
<a href="/products">Products</a>
<a href="/contact">Contact</a>
</nav>
The role="list" attribute is redundant on an <ol> element because it already has an implicit ARIA role of list.
HTML elements come with built-in (implicit) ARIA roles that convey their purpose to assistive technologies. The <ol> and <ul> elements both have an implicit role of list, so explicitly adding role="list" is unnecessary and creates noise in your markup.
That said, there’s a well-known reason some developers add this role intentionally. Safari removes list semantics when list-style: none is applied via CSS. Adding role="list" is a common workaround to restore those semantics for VoiceOver users. If this is your situation, the W3C warning is technically correct but you may choose to keep the role for accessibility reasons.
If you don’t need the Safari workaround, simply remove the role attribute.
Before
<ol role="list">
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>
After
<ol>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ol>
The HTML specification assigns implicit ARIA roles to many elements, meaning browsers and assistive technologies already understand their purpose without any extra attributes. The ul element has a built-in role of list, the nav element has a role of navigation, the button element has a role of button, and so on. When you explicitly add a role that matches the element’s implicit role, it creates redundancy that the validator warns about.
This principle is formalized as the first rule of ARIA use: do not use ARIA if a native HTML element already provides the semantics you need. Adding redundant ARIA roles clutters your markup, can confuse developers maintaining the code, and in rare edge cases may cause assistive technologies to announce information twice or behave unexpectedly.
This same warning applies to other elements with implicit roles, such as adding role="navigation" to a nav element, role="banner" to a header element, or role="contentinfo" to a footer element.
A note about Safari and list-style: none
There is one well-known exception worth mentioning. Safari intentionally removes list semantics from ul and ol elements when list-style: none is applied via CSS. This means VoiceOver on macOS and iOS will not announce the element as a list. In this specific case, some developers deliberately add role="list" to restore the list semantics. While the W3C validator will still flag it as redundant (since it evaluates HTML in isolation, without considering CSS), this is a legitimate accessibility pattern where the redundant role serves a real purpose. If you’re in this situation, you may choose to keep role="list" and accept the validator warning.
Examples
Incorrect: redundant role="list" on ul
<ul role="list">
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
Correct: relying on implicit semantics
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
Incorrect: other common redundant roles
<nav role="navigation">
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main role="main">
<h1>Welcome</h1>
</main>
<footer role="contentinfo">
<p>© 2024 Example Inc.</p>
</footer>
Correct: native elements without redundant roles
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
<h1>Welcome</h1>
</main>
<footer>
<p>© 2024 Example Inc.</p>
</footer>
Acceptable exception: restoring semantics removed by CSS
If your stylesheet strips list markers and you need to preserve list semantics for screen readers, the redundant role is a pragmatic choice:
<!-- list-style: none is applied via CSS, which removes semantics in Safari -->
<ul role="list" class="unstyled-list">
<li>Step one</li>
<li>Step two</li>
<li>Step three</li>
</ul>
In this case, you can suppress or ignore the validator warning, understanding that it serves an accessibility need that the validator cannot detect from the HTML alone.
The listbox role is the implicit ARIA role for a <select> element only when it has a multiple attribute or a size attribute greater than 1. A standard single-selection <select> (dropdown) has an implicit role of combobox, so explicitly assigning role="listbox" to it creates a conflict.
When a <select> element has no multiple attribute and no size greater than 1, browsers render it as a collapsed dropdown — a combobox. The listbox role describes a widget where all options are persistently visible, which matches the behavior of a multi-select or a select with a visible size greater than 1. Applying role="listbox" to a standard dropdown misrepresents the control to assistive technologies.
You have a few options to fix this: remove the role="listbox" entirely (since the browser already assigns the correct implicit role), add the multiple attribute, or set size to a value greater than 1.
Incorrect Example
<select role="listbox" name="color">
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
Fixed Examples
Remove the explicit role and let the browser handle it:
<select name="color">
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
Or, if you genuinely need role="listbox", use multiple or size greater than 1:
<select role="listbox" name="color" multiple>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
<select role="listbox" name="color" size="3">
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
</select>
In most cases, simply removing role="listbox" is the best fix. The implicit ARIA roles already convey the correct semantics to assistive technologies without any extra attributes.
Many HTML elements have built-in (implicit) ARIA roles defined by the WAI-ARIA specification. The <li> element natively carries the listitem role when it is a child of a <ul>, <ol>, or <menu> element. Adding role="listitem" explicitly doesn’t change behavior, but it clutters your markup and signals a misunderstanding of how semantic HTML and ARIA interact. This falls under the first rule of ARIA use: “If you can use a native HTML element with the semantics and behavior you require already built in, do so, instead of re-purposing an element and adding an ARIA role.”
Redundant ARIA roles create several problems:
- Maintenance burden — Extra attributes add noise to your code, making it harder to read and maintain.
- Potential confusion — Other developers may wonder if the explicit role was added intentionally to override something, leading to uncertainty during code reviews.
- Validator warnings — Tools like the W3C HTML Validator flag these redundancies, and accumulating unnecessary warnings can obscure real issues that need attention.
The ARIA listitem role is designed for situations where you cannot use semantic HTML — for instance, when you need to create a list-like structure from generic elements like <div> or <span>. In those cases, you would pair role="list" on the container with role="listitem" on each child. But when you’re already using <ul>, <ol>, or <menu> with <li> children, the ARIA roles are built in and should not be repeated.
To fix this, simply remove the role="listitem" attribute from your <li> elements. If you also have role="list" on a <ul> or <ol>, remove that too — it’s equally redundant.
Examples
❌ Redundant role on <li> elements
<ul role="list">
<li role="listitem">Apples</li>
<li role="listitem">Bananas</li>
<li role="listitem">Cherries</li>
</ul>
Both role="list" on the <ul> and role="listitem" on each <li> are unnecessary because these elements already carry those roles implicitly.
✅ Clean semantic HTML without redundant roles
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Cherries</li>
</ul>
The <ul> and <li> elements provide all the accessibility semantics needed without any explicit ARIA attributes.
✅ Using ARIA roles on non-semantic elements (when necessary)
If for some reason you cannot use native list elements, ARIA roles are appropriate on generic elements:
<div role="list">
<div role="listitem">Apples</div>
<div role="listitem">Bananas</div>
<div role="listitem">Cherries</div>
</div>
This is the intended use case for role="listitem" — adding list semantics to elements that don’t have them natively. However, using semantic <ul>/<ol> with <li> is always preferred when possible.
The ARIA specification defines a set of roles that convey the purpose of an element to assistive technologies like screen readers. Many HTML elements have implicit ARIA roles — built-in semantics that map directly to ARIA roles without any extra markup. The <main> element is one of these: it automatically communicates the main landmark role to assistive technologies.
When you write <main role="main">, you’re explicitly stating something the browser and assistive technologies already know. The W3C validator warns about this redundancy because it can signal a misunderstanding of how native HTML semantics work. While it won’t break anything, unnecessary attributes add noise to your markup and can make code harder to maintain.
This principle applies broadly across HTML. For example, <nav> implicitly has role="navigation", <header> implicitly has role="banner" (when not nested inside a sectioning element), and <button> implicitly has role="button". Explicitly restating these roles is discouraged by both the W3C and the ARIA in HTML specification, which states: “Setting an ARIA role and/or `aria-` attribute that matches the implicit ARIA semantics is unnecessary and is NOT RECOMMENDED.”*
Why this matters
- Code clarity: Redundant attributes make your HTML harder to read and can confuse other developers into thinking the attribute is necessary.
- Standards compliance: The W3C validator raises a warning, which can obscure more important issues in your validation reports.
- Best practices: Following the principle of using native HTML semantics without redundant ARIA keeps your code clean and aligns with the first rule of ARIA: “If you can use a native HTML element with the semantics and behavior you require already built in, do so, instead of re-purposing an element and adding an ARIA role.”
How to fix it
Remove the role="main" attribute from any <main> element. The semantic meaning is already provided by the element itself.
If you’re working with a <div> or another generic element that needs the main landmark role (for example, in a legacy codebase that cannot use <main>), then role="main" is appropriate and necessary on that element.
Examples
❌ Redundant role on <main>
<main role="main">
<h1>Welcome to my site</h1>
<p>This is the primary content of the page.</p>
</main>
The role="main" attribute is unnecessary here because <main> already implies it.
✅ Using <main> without a redundant role
<main>
<h1>Welcome to my site</h1>
<p>This is the primary content of the page.</p>
</main>
✅ Using role="main" on a non-semantic element (when necessary)
<div role="main">
<h1>Welcome to my site</h1>
<p>This is the primary content of the page.</p>
</div>
This approach is valid when you cannot use the <main> element — for instance, due to framework constraints or legacy browser support requirements. In most modern projects, prefer the <main> element instead.
The HTML specification defines certain elements as having implicit ARIA roles — roles that are automatically communicated to assistive technologies without any additional attributes. The nav element is one of these: its implicit role is navigation. When you explicitly add role="navigation" to a nav element, you’re telling the browser something it already knows, which clutters your markup without adding any value.
This redundancy matters for several reasons:
- Code maintainability: Unnecessary attributes make your HTML harder to read and maintain. Future developers may wonder if the explicit role is there for a specific reason, creating confusion.
- Standards compliance: The W3C validator warns about this because the ARIA specification follows a principle often summarized as the first rule of ARIA: don’t use ARIA if a native HTML element already provides the semantics you need. Extending this principle, don’t re-declare semantics that are already present.
- No accessibility benefit: Assistive technologies like screen readers already recognize nav as a navigation landmark. Adding the explicit role doesn’t improve the experience for users of these technologies — it’s simply noise.
The role="navigation" attribute is useful when applied to a non-semantic element like a div or span that functions as navigation but can’t be changed to a nav element (for example, due to legacy constraints). But when you’re already using nav, the attribute is unnecessary.
To fix this, remove the role="navigation" attribute from your nav element. The semantic meaning is fully preserved.
Examples
Incorrect: redundant role on nav
This triggers the W3C validator warning because the navigation role is already implicit:
<nav role="navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
Correct: nav without the explicit role
Simply remove the redundant role attribute:
<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: using role="navigation" on a non-semantic element
If you cannot use a nav element, applying the role to a div is a valid approach. This does not trigger the warning:
<div role="navigation">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</div>
Correct: labeling multiple nav elements
When a page has more than one nav, use aria-label or aria-labelledby to differentiate them for assistive technology users — but still don’t add the redundant role:
<nav aria-label="Main">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</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>
A <tr> element already has an implicit ARIA role of row, so adding role="row" is redundant when the parent <table> uses its default semantics or has a role of table, grid, or treegrid.
HTML tables come with built-in accessibility semantics. The <table> element implicitly has role="table", and <tr> implicitly has role="row". Browsers and assistive technologies already understand this structure, so explicitly adding these roles is unnecessary and flagged by the W3C validator.
The only time you’d need to add a role to a <tr> is when the table’s native semantics have been overridden — for example, if the <table> has been repurposed with a non-table role like role="presentation" or role="none". In that case, you’d need explicit ARIA roles to restore row semantics.
Incorrect Example
<table>
<tr role="row">
<th>Name</th>
<th>Email</th>
</tr>
<tr role="row">
<td>Alice</td>
<td>alice@example.com</td>
</tr>
</table>
Fixed Example
Simply remove the redundant role="row" from the <tr> elements:
<table>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
<tr>
<td>Alice</td>
<td>alice@example.com</td>
</tr>
</table>
The same fix applies if your <table> explicitly has role="table", role="grid", or role="treegrid" — the <tr> elements still don’t need an explicit role="row" because the browser infers it automatically.
The role attribute is not allowed on a label element when that label is associated with a form control (a labelable element) through the for attribute or by nesting.
When a label is associated with a form control, the browser already understands its purpose — it’s a label. Adding a role attribute overrides this native semantics, which is redundant at best and confusing for assistive technologies at worst.
A label becomes “associated” with a labelable element in two ways: explicitly via the for attribute pointing to the control’s id, or implicitly by wrapping the control inside the label. Labelable elements include input (except type="hidden"), select, textarea, button, meter, output, and progress.
If the label is associated, simply remove the role attribute. The native semantics are already correct and sufficient.
If you truly need a custom role for some reason and the label is not functionally labeling a control, you can disassociate it by removing the for attribute or unnesting the control — but this is rarely the right approach.
Invalid Example
<label for="email" role="presentation">Email</label>
<input type="email" id="email">
Valid Example
<label for="email">Email</label>
<input type="email" id="email">
When you use semantic HTML elements, browsers automatically assign appropriate ARIA roles behind the scenes. An <input type="text"> element without a list attribute is inherently recognized by browsers and assistive technologies as a textbox — a control that accepts free-form text input. Explicitly declaring role="textbox" on such an element repeats information that is already conveyed natively, which is what the validator flags.
The distinction about the list attribute matters because when an <input type="text"> does have a list attribute (linking it to a <datalist>), its implicit role changes to combobox rather than textbox. In that scenario, a role="textbox" would not only be redundant — it would actually be incorrect. The validator’s message specifically targets the case where there is no list attribute, meaning the implicit role is already textbox.
Why this is a problem
- Redundancy clutters your code. Adding roles that elements already possess makes HTML harder to read and maintain without providing any benefit.
- Potential for confusion. Other developers (or your future self) may wonder if the explicit role was added intentionally to override some other behavior, leading to unnecessary investigation.
- Standards compliance. The W3C and WAI-ARIA authoring practices recommend against setting ARIA roles that duplicate the native semantics of an element. The first rule of ARIA use is: “If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.”
- No accessibility benefit. Assistive technologies already understand that <input type="text"> is a textbox. The explicit role adds no additional information for screen readers or other tools.
How to fix it
Simply remove the role="textbox" attribute from your <input type="text"> element. The native semantics of the element are sufficient.
If you’ve added the role because the input is styled or behaves differently, consider whether you actually need a different element or a different ARIA pattern instead.
Examples
❌ Incorrect: redundant role="textbox"
<label for="username">Username</label>
<input type="text" id="username" role="textbox">
The role="textbox" is unnecessary here because <input type="text"> without a list attribute already has an implicit role of textbox.
✅ Correct: no explicit role needed
<label for="username">Username</label>
<input type="text" id="username">
✅ Also correct: input with list attribute (different implicit role)
<label for="color">Favorite color</label>
<input type="text" id="color" list="colors">
<datalist id="colors">
<option value="Red">
<option value="Green">
<option value="Blue">
</datalist>
In this case, the list attribute changes the implicit role to combobox, so the validator warning about a redundant textbox role would not apply. Note that adding role="textbox" here would be incorrect rather than merely redundant, since it would override the proper combobox semantics.
❌ Incorrect: redundant role on implicit text input
<label for="search-field">Search</label>
<input id="search-field" role="textbox">
When the type attribute is omitted, <input> defaults to type="text", so the implicit role is still textbox and the explicit role remains redundant.
✅ Correct: let the default type handle semantics
<label for="search-field">Search</label>
<input id="search-field">
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