HTML Guides
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 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 longdesc attribute was originally designed to point to a URL containing a detailed description of an element's content, primarily as an accessibility feature for screen reader users. On <iframe> elements, it was intended to describe the framed content for users who couldn't perceive it directly. However, the attribute was poorly implemented across browsers, rarely used by assistive technologies, and was ultimately removed from the HTML specification for <iframe> elements.
Using obsolete attributes causes W3C validation errors and can create a false sense of accessibility. Since browsers and assistive technologies don't reliably process longdesc on iframes, users who need the description may never actually reach it. A visible link, on the other hand, is universally accessible — it works for all users regardless of their browser, device, or assistive technology.
The fix is straightforward: remove the longdesc attribute from the <iframe> and place a regular <a> element near the iframe that links to the description page. This approach is more robust because the link is visible, discoverable, and works everywhere.
Examples
❌ Obsolete: Using longdesc on an <iframe>
<iframe src="report.html" title="Annual report" longdesc="report-description.html"></iframe>
This triggers the validation error because longdesc is no longer a valid attribute on <iframe>.
✅ Fixed: Using a visible link instead
<iframe src="report.html" title="Annual report"></iframe>
<p>
<a href="report-description.html">Read a detailed description of the annual report</a>
</p>
The longdesc attribute is removed, and a descriptive link is placed adjacent to the iframe so all users can access it.
✅ Alternative: Wrapping in a <figure> for better semantics
For a more structured approach, you can use a <figure> element with a <figcaption> to associate the description link with the iframe:
<figure>
<iframe src="report.html" title="Annual report"></iframe>
<figcaption>
Annual report overview.
<a href="report-description.html">Read the full description</a>.
</figcaption>
</figure>
This groups the iframe and its caption together semantically, making the relationship between them clear to both sighted users and assistive technologies.
✅ Alternative: Using aria-describedby for additional context
If you want to provide a short inline description that assistive technologies can announce automatically, you can use aria-describedby:
<p id="report-desc">
This iframe contains the interactive annual report for 2024.
<a href="report-description.html">Read the full description</a>.
</p>
<iframe src="report.html" title="Annual report" aria-describedby="report-desc"></iframe>
This links the iframe to the descriptive paragraph programmatically. Screen readers will announce the content of the referenced element when the iframe receives focus, while the visible link remains available to all users.
Key Takeaways
- Always include a
titleattribute on<iframe>elements to describe their purpose — this is an important baseline accessibility practice. - Replace
longdescwith a visible<a>element that links to the long description. - Place the link near the iframe so users can easily find it.
- Consider using
<figure>and<figcaption>oraria-describedbyfor stronger semantic association between the iframe and its description.
The longdesc attribute dates back to HTML4, where it accepted a URL pointing to a separate page (or section of a page) containing a detailed description of the image. The idea was to supplement the short text in the alt attribute with a more comprehensive explanation, particularly useful for complex images like charts, diagrams, or infographics.
HTML5 made longdesc obsolete for several reasons. Browser support was inconsistent — most browsers never exposed the attribute in a way that was easily discoverable by users. Many developers misused it by placing literal descriptions in the attribute instead of URLs, or left it pointing to broken links. Because the attribute was invisible in the rendered page, there was no visual indication that a longer description existed, making it practically useless for sighted users and unreliable for assistive technology users.
The recommended replacements are more robust and accessible:
- Wrap the image in an
aelement (or place a link nearby) that points to the description page. This makes the link visible and usable by everyone. - Use
aria-describedbyto reference a description that already exists on the same page. This is ideal when the detailed description is displayed alongside the image. - Use a
figurewithfigcaptionto associate a visible caption or description directly with the image.
These approaches are better for accessibility because they work reliably across browsers and assistive technologies, and they make the description discoverable to all users, not just those using specific screen readers that happened to support longdesc.
Examples
❌ Obsolete: using longdesc
<img
src="cat.jpg"
alt="Smiling cat sitting on a windowsill"
longdesc="descriptions/smiling-cat.html">
This triggers the validation error because longdesc is no longer a valid attribute on img in HTML5.
✅ Fix: wrap the image in a link
The simplest replacement is to make the image itself a link to the description:
<a href="descriptions/smiling-cat.html">
<img src="cat.jpg" alt="Smiling cat sitting on a windowsill">
</a>
✅ Fix: provide a separate link near the image
If you don't want the image itself to be clickable, place a visible link nearby:
<figure>
<img src="chart.png" alt="Quarterly revenue chart for 2024">
<figcaption>
Quarterly revenue chart.
<a href="descriptions/revenue-chart.html">View detailed description</a>
</figcaption>
</figure>
✅ Fix: use aria-describedby for on-page descriptions
When the long description is already on the same page, reference it with aria-describedby:
<figure>
<img
src="chart.png"
alt="Quarterly revenue chart for 2024"
aria-describedby="chart-description">
<figcaption id="chart-description">
Revenue grew from $2.1M in Q1 to $3.8M in Q4, with the largest
quarter-over-quarter increase occurring between Q2 and Q3.
</figcaption>
</figure>
This approach keeps the description visible on the page and programmatically associates it with the image for screen readers.
Choosing the right approach
| Scenario | Recommended approach |
|---|---|
| Description is on a separate page | Wrap image in an a element or add a nearby link |
| Description is visible on the same page | Use aria-describedby pointing to the description's id |
| Image needs a brief visible caption | Use figure with figcaption |
| Complex image (chart, diagram, infographic) | Combine figure, figcaption, and a link to a full description |
In all cases, make sure the alt attribute still provides a meaningful short description. The long description supplements alt — it doesn't replace it.
The <main> element serves a very specific purpose in HTML: it identifies the primary content of the document's <body> — the content that is directly related to or expands upon the central topic of the page. Because of this document-level role, the HTML specification restricts where <main> can appear. It must not be a descendant of <article>, <aside>, <header>, <footer>, or <nav>. These are all sectioning or structural elements that represent subsets of the page, and nesting <main> inside them creates a semantic contradiction — you'd be saying "the main content of the whole page lives inside this one sub-section."
The <article> element, by contrast, represents a self-contained composition — something like a blog post, a news story, a forum post, or a comment. Articles are meant to be independently distributable or reusable. They logically live within the main content area of a page, not around it.
This distinction matters for accessibility. Screen readers and assistive technologies use the <main> landmark to let users skip directly to the primary content of a page. When <main> is incorrectly nested inside an <article>, assistive technologies may misinterpret the document structure, making navigation confusing or unreliable. Search engines also rely on semantic HTML to understand page structure, so incorrect nesting can affect how your content is indexed.
To fix this issue, move the <main> element out of the <article> and make it a direct child of <body> (or of a non-sectioning element like <div>). Then place your <article> elements inside <main>. Also remember that only one visible <main> element should exist per page (additional <main> elements must have the hidden attribute).
Examples
Incorrect: <main> nested inside <article>
This triggers the validation error because <main> is a descendant of <article>:
<article>
<main>
<h1>My Blog Post</h1>
<p>This is the post content.</p>
</main>
</article>
Incorrect: <main> deeply nested inside <article>
The error also triggers when <main> is an indirect descendant — it doesn't need to be a direct child:
<article>
<div class="wrapper">
<main>
<h1>My Blog Post</h1>
<p>This is the post content.</p>
</main>
</div>
</article>
Correct: <article> inside <main>
Invert the relationship so that <main> wraps the article content:
<main>
<article>
<h1>My Blog Post</h1>
<p>This is the post content.</p>
</article>
</main>
Correct: Multiple articles inside <main>
A typical page layout with <main> containing several articles alongside other content:
<main>
<h1>Latest Posts</h1>
<article>
<h2>First Post</h2>
<p>Content of the first post.</p>
</article>
<article>
<h2>Second Post</h2>
<p>Content of the second post.</p>
</article>
</main>
Correct: Full document structure
A complete valid document showing the proper placement of <main> as a direct child of <body>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Blog - Latest Posts</title>
</head>
<body>
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<h1>Latest Posts</h1>
<article>
<h2>My Blog Post</h2>
<p>This is the post content.</p>
</article>
</main>
<footer>
<p>© 2024 My Blog</p>
</footer>
</body>
</html>
The key rule to remember: <main> represents the page's primary content and sits at the top of your content hierarchy. Sectioning elements like <article>, <aside>, and <nav> are components within that hierarchy and belong inside or alongside <main> — never around it.
The <main> element identifies the primary content of a page — the content that is directly related to or expands upon the central topic of the document. According to the WHATWG HTML living standard, a <main> element must not appear as a descendant of another <main> element. This rule exists because the semantic purpose of <main> is to mark a single, unique content region; nesting it creates a contradictory structure where one "primary content area" exists inside another.
This matters for several important reasons:
- Accessibility: Screen readers and other assistive technologies use the
<main>landmark to allow users to skip directly to the primary content. When multiple or nested<main>elements exist, this navigation breaks down — assistive technology may only recognize one of them, or it may present a confusing hierarchy of "main" landmarks to the user. - Standards compliance: Browsers and validators enforce the HTML specification's content model for
<main>. A nested<main>violates that content model and produces a validation error. - Semantic clarity: The
<main>element carries specific meaning. Nesting it dilutes that meaning and signals a structural misunderstanding of the document to both machines and other developers.
This issue commonly arises when composing pages from multiple templates or components — for example, when a layout template already wraps content in <main> and an inner component or partial also includes its own <main> element. It can also happen during refactoring when code is moved between files without checking the surrounding structure.
To fix the issue, identify the nested <main> element and replace it with a more appropriate element. If the inner content represents a thematic grouping, use <section>. If it represents a self-contained composition (like a blog post or comment), use <article>. If no particular semantic meaning is needed, a plain <div> works fine.
Examples
❌ Invalid: nested <main> elements
<main>
<h1>Welcome</h1>
<main>
<p>This nested main element is invalid.</p>
</main>
</main>
The inner <main> is a descendant of the outer <main>, which violates the content model.
✅ Fixed: inner <main> replaced with <section>
<main>
<h1>Welcome</h1>
<section>
<h2>Introduction</h2>
<p>This section is valid inside main.</p>
</section>
</main>
❌ Invalid: deeply nested <main> inside other elements
The nesting doesn't have to be direct. A <main> anywhere inside another <main> triggers this error:
<main>
<h1>Dashboard</h1>
<div class="content-wrapper">
<article>
<main>
<p>Still invalid, even though it's nested several levels deep.</p>
</main>
</article>
</div>
</main>
✅ Fixed: replaced with <div>
<main>
<h1>Dashboard</h1>
<div class="content-wrapper">
<article>
<div>
<p>Now valid with a neutral container element.</p>
</div>
</article>
</div>
</main>
❌ Invalid: component templates each providing <main>
This pattern often appears in frameworks where a layout and a page component both define <main>:
<!-- Layout template wraps page content -->
<main>
<!-- Page component output -->
<main>
<h1>About Us</h1>
<p>Our story begins...</p>
</main>
</main>
✅ Fixed: <main> only in the layout
<!-- Layout template wraps page content -->
<main>
<!-- Page component output -->
<h1>About Us</h1>
<p>Our story begins...</p>
</main>
Keep <main> at whichever level makes the most sense for your architecture — typically the outermost layout — and remove it from inner components. If you need to group the inner content, use <section>, <article>, or <div> instead.
The <main> element serves a specific structural role: it identifies the primary content of the page, distinct from repeated elements like headers, footers, and navigation. Because of this unique purpose, the HTML specification strictly limits where <main> can appear in the document tree. Nesting <main> inside a <section> element violates these rules because <section> represents a thematic grouping of content — placing <main> inside it implies that the dominant page content is merely a subsection of something else, which is semantically contradictory.
According to the WHATWG HTML living standard, a hierarchically correct <main> element is one whose ancestor elements are limited to <html>, <body>, <div>, <form> (without an accessible name), and autonomous custom elements. This means <main> cannot be a descendant of <article>, <aside>, <footer>, <header>, <nav>, or <section>.
Why this matters
- Accessibility: Screen readers and assistive technologies use the
<main>element as a landmark to let users skip directly to the primary content. When<main>is incorrectly nested inside<section>, assistive technologies may misinterpret the document structure, making navigation harder for users who rely on landmarks. - Standards compliance: Browsers are lenient and will render the page regardless, but the semantic meaning is broken. Future browser features or tools that depend on correct document structure may not work as expected.
- Document structure clarity: The
<main>element should clearly sit at the top level of your content hierarchy, making it immediately obvious to both developers and machines which part of the page is the primary content.
Additional rules for <main>
Beyond the ancestor restriction, remember that a document must not have more than one visible <main> element. If you use multiple <main> elements (for example, in a single-page application), all but one must have the hidden attribute specified.
Examples
Incorrect: <main> nested inside <section>
This structure places <main> as a descendant of <section>, which triggers the validation error:
<body>
<header>
<h1>My Website</h1>
</header>
<section>
<main>
<h2>Welcome</h2>
<p>This is the primary content of the page.</p>
</main>
</section>
</body>
Correct: <main> as a sibling of <section>
Move <main> out of the <section> so it is a direct child of <body>:
<body>
<header>
<h1>My Website</h1>
</header>
<main>
<h2>Welcome</h2>
<p>This is the primary content of the page.</p>
</main>
<section>
<h2>Related Topics</h2>
<p>Additional thematic content goes here.</p>
</section>
</body>
Correct: <section> elements inside <main>
If your primary content is divided into thematic sections, nest the <section> elements inside <main> — not the other way around:
<body>
<header>
<h1>My Website</h1>
</header>
<main>
<section>
<h2>Introduction</h2>
<p>An overview of the topic.</p>
</section>
<section>
<h2>Details</h2>
<p>A deeper dive into the subject.</p>
</section>
</main>
<footer>
<p>© 2024 My Website</p>
</footer>
</body>
Correct: <main> wrapped in a <div>
If your layout requires a wrapper element around <main>, a <div> is a valid ancestor:
<body>
<div class="layout-wrapper">
<header>
<h1>My Website</h1>
</header>
<main>
<h2>Welcome</h2>
<p>This is the primary content of the page.</p>
</main>
</div>
</body>
The key principle is simple: <main> defines the dominant content of the entire document, so it belongs at the top level of your content hierarchy. Sectioning elements like <section>, <article>, <aside>, <nav>, <header>, and <footer> should never wrap <main> — instead, they should be placed as children or siblings of it.
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 name attribute on the <a> element is obsolete in HTML5 and must be replaced with the id attribute.
Older versions of HTML used <a name="section1"> to create anchor targets within a page. HTML5 dropped this approach. The id attribute on any element now serves the same purpose, and it works on <a> tags as well as <div>, <section>, <h2>, or any other element. Fragment links like href="#section1" will scroll to whatever element has id="section1", regardless of element type.
The name attribute is still valid on elements like <input>, <form>, <meta>, and <map>, where it has a distinct function. On <a>, though, it has no valid use in modern HTML.
Invalid example
<a name="about">About us</a>
<p>Read more in our <a href="#about">about section</a>.</p>
Valid example
<h2 id="about">About us</h2>
<p>Read more in our <a href="#about">about section</a>.</p>
If the anchor wraps content that has no better semantic element, a <span> or <div> with an id works fine:
<span id="about">About us</span>
The name attribute is not a valid attribute for the <div> element.
The name attribute is only allowed on specific HTML elements that have a defined use for it, such as <input>, <form>, <iframe>, <object>, <map>, <select>, <textarea>, and <meta>. On these elements, name serves a purpose: identifying form data sent to a server, targeting frames, or referencing image maps.
The <div> element does not accept name. If the goal is to identify or reference a <div>, use the id attribute instead. The id attribute is a global attribute, valid on any HTML element, and provides a unique identifier for styling with CSS, targeting with JavaScript, or linking with fragment URLs.
If the name attribute was being used as a hook for document.getElementsByName(), switching to id with document.getElementById() (or using a class with document.querySelectorAll()) is the correct approach.
Invalid example
<div name="sidebar">
<p>Some content</p>
</div>
Valid example
<div id="sidebar">
<p>Some content</p>
</div>
The name attribute on the <img> element is obsolete in HTML5 and must be replaced with the id attribute.
Older pages used <img name="logo"> so scripts could reach the image through document.images["logo"], a pattern common in pre-DOM rollover scripts. HTML5 dropped the attribute. An id serves the same purpose: document.getElementById("logo") finds the image, and document.images also indexes images by their id.
The name attribute is still valid on elements like <input>, <form>, <meta>, and <map>, where it has a distinct function. On <img>, though, it has no valid use in modern HTML.
Invalid example
<img src="logo.png" name="logo" alt="Acme logo">
Valid example
<img src="logo.png" id="logo" alt="Acme logo">
Any script that referenced the image as document.images["logo"] keeps working with the id in place, or can switch to document.getElementById("logo").
The name attribute is reported on the <param> element because <param> itself is obsolete in the HTML standard.
<param> used to sit inside <object> to pass named values to plugins such as Flash, Java applets, or ActiveX controls. Each one carried a name and a value. Browsers dropped plugin support, the element was removed from the HTML specification, and the Nu validator now rejects its attributes, including name.
There is no replacement attribute to add. The fix is to delete the <param> element. If the surrounding <object> points at a resource that browsers still handle on their own, like an image or a PDF, set the data and type attributes directly on <object> and drop the parameters. If the content depended on a plugin, use a native element such as <video>, <audio>, or <iframe> instead.
Invalid example
<object data="movie.swf" type="application/x-shockwave-flash">
<param name="autoplay" value="true">
</object>
Valid example
<video src="movie.mp4" autoplay controls></video>
The name attribute is not a valid attribute for the <span> element in HTML.
The name attribute exists on specific elements like <a>, <form>, <input>, <map>, <meta>, <iframe>, <object>, and <param>. It was never part of the <span> element's specification. In older HTML practices, <a name="section"> was used to create anchor targets within a page, and some developers incorrectly applied the same pattern to <span> elements.
To create an anchor target on a <span> or any other element, use the id attribute instead. The id attribute is a global attribute, valid on every HTML element, and works as a fragment identifier in URLs (e.g., page.html#section-name).
If the name attribute was being used for JavaScript targeting or styling, switch to id, class, or data-* attributes depending on the use case.
Invalid example
<span name="intro">Welcome to the site.</span>
Valid examples
Use id to create a linkable anchor target:
<span id="intro">Welcome to the site.</span>
Use class when multiple elements share the same label:
<span class="intro">Welcome to the site.</span>
Use a data-* attribute for custom metadata consumed by JavaScript:
<span data-name="intro">Welcome to the site.</span>
The name attribute is not a valid attribute for the <table> element in HTML.
The name attribute was never part of any HTML specification for tables. Some older browsers tolerated it as a way to create anchor targets, but validators reject it because it does not belong on <table>. If the goal is to link directly to a table, use the id attribute instead. The id attribute is a global attribute, valid on any HTML element, and works as a fragment identifier in URLs (e.g., page.html#prices).
If the name attribute was being used for JavaScript access, switch to id and use document.getElementById(), or use a data-* attribute for storing custom metadata.
Invalid example
<table name="prices">
<tr>
<td>Item</td>
<td>Price</td>
</tr>
</table>
Valid example
Using id as a fragment target:
<table id="prices">
<tr>
<td>Item</td>
<td>Price</td>
</tr>
</table>
If custom metadata is needed rather than a link target, a data-* attribute works:
<table data-name="prices">
<tr>
<td>Item</td>
<td>Price</td>
</tr>
</table>
The name attribute is not a valid attribute for the <td> element in HTML.
The name attribute was never part of any HTML specification for table cells. Some older browsers tolerated it as a way to create anchor targets within a table, but validators reject it because it does not belong on <td>. If the goal is to create a link target so users can jump to a specific cell, use the id attribute instead. The id attribute is a global attribute, valid on any HTML element, and works as a fragment identifier in URLs (e.g., page.html#cell1).
If the name attribute was being used for JavaScript access, switch to id and use document.getElementById(), or use a data-* attribute for storing custom metadata.
Invalid example
<table>
<tr>
<td name="cell1">Data</td>
</tr>
</table>
Valid example
Using id as a fragment target:
<table>
<tr>
<td id="cell1">Data</td>
</tr>
</table>
If custom metadata is needed rather than a link target, a data-* attribute works:
<table>
<tr>
<td data-name="cell1">Data</td>
</tr>
</table>
Before HTML5, the way to create a link target within a page was to use a "named anchor" — an <a> element with a name attribute. This allowed other links to jump to that specific spot using a fragment identifier (e.g., href="#section-5"). In HTML5, the name attribute on <a> elements has been marked as obsolete. Instead, the id attribute on any element serves this purpose, making the extra <a> wrapper unnecessary.
This matters for several reasons:
- Standards compliance: Using obsolete attributes means your markup doesn't conform to the current HTML specification, which can cause W3C validation errors.
- Cleaner markup: Named anchors add an extra element that serves no semantic purpose. Using
iddirectly on the target element is simpler and more meaningful. - Accessibility: Screen readers and assistive technologies work better with semantic HTML. An
idon a heading or<section>provides clearer document structure than a nested anchor element. - Browser behavior: While browsers still support
namefor backward compatibility, relying on obsolete features is risky as future browser versions may change or drop support.
To fix this, remove the <a name="..."> element and place an id attribute directly on the nearest appropriate container element, such as a heading (<h2>, <h3>, etc.), a <section>, or a <div>.
Examples
Incorrect: using the obsolete name attribute
<h2>
<a name="section-5">Section 5</a>
</h2>
The <a> element here exists solely to create a link target, adding unnecessary markup.
Correct: using id on the heading
<h2 id="section-5">Section 5</h2>
The id attribute on the <h2> makes it a valid fragment link target without any extra elements.
Linking to the section
Both approaches allow navigation via the same fragment URL. The link syntax doesn't change:
<a href="#section-5">Jump to Section 5</a>
Using id on other container elements
The id attribute works on any HTML element, so you can place it wherever makes the most sense semantically:
<section id="contact-info">
<h2>Contact Information</h2>
<p>Email us at hello@example.com.</p>
</section>
Incorrect: multiple named anchors in a document
<p>
<a name="intro">Welcome to our page.</a>
</p>
<p>
<a name="conclusion">Thanks for reading.</a>
</p>
Correct: replacing all named anchors with id attributes
<p id="intro">Welcome to our page.</p>
<p id="conclusion">Thanks for reading.</p>
Remember that id values must be unique within a document — no two elements can share the same id. If you're migrating from name attributes, check for duplicates and ensure each id is used only once.
The name attribute on <a> elements is obsolete in HTML5 and should be replaced with the id attribute.
In older versions of HTML, the name attribute on anchor elements was used to create fragment identifiers — targets you could link to with #section-name in a URL. In HTML5, this approach has been deprecated in favor of the id attribute, which can be placed on any element, not just <a> tags.
Using id is more flexible because you can turn any element into a link target directly, without wrapping it in an anchor. The id attribute works the same way for fragment navigation: a link pointing to #section-name will scroll to the element with id="section-name".
HTML Examples
❌ Obsolete usage with name
<a name="about"></a>
<h2>About Us</h2>
<p>Welcome to our site.</p>
<a href="#about">Go to About</a>
✅ Fixed using id
<h2 id="about">About Us</h2>
<p>Welcome to our site.</p>
<a href="#about">Go to About</a>
The id attribute is placed directly on the <h2> heading, eliminating the need for an empty <a> tag entirely. The #about link works exactly the same way.
The name attribute was historically used on img elements to reference images through JavaScript's document.images collection or via document.getElementsByName(). In early HTML, name served as an identifier for various elements before the id attribute was widely adopted. The HTML living standard (WHATWG) now marks name as obsolete on img elements, meaning it should no longer be used in new content.
This matters for several reasons:
- Standards compliance: Using obsolete attributes means your HTML does not conform to the current specification, which can cause validation errors and may lead to unexpected behavior in future browser versions.
- Consistency: The
idattribute is the universal mechanism for uniquely identifying any HTML element. Usingidinstead ofnamekeeps your markup consistent and predictable. - JavaScript and CSS targeting: Modern APIs like
document.getElementById()anddocument.querySelector()work withid, notnameon image elements. CSS selectors also target elements byid(e.g.,#myImage), makingidthe more versatile choice. - Fragment linking: The
idattribute allows you to link directly to an element using a URL fragment (e.g.,page.html#myImage), whereas the obsoletenameattribute onimgdoes not serve this purpose.
To fix this issue, simply replace name with id on your img elements. Keep in mind that id values must be unique within the entire document — no two elements can share the same id. If you have JavaScript code that references the image by name (e.g., document.images["myImage"] or document.getElementsByName("myImage")), update those references to use document.getElementById("myImage") or document.querySelector("#myImage") instead.
Examples
Incorrect: using the obsolete name attribute
<img src="photo.jpg" name="heroImage" alt="A sunset over the ocean">
This triggers the validation error because name is no longer a valid attribute on img.
Correct: using the id attribute
<img src="photo.jpg" id="heroImage" alt="A sunset over the ocean">
The name attribute is replaced with id, and the element can now be targeted with document.getElementById("heroImage") or the CSS selector #heroImage.
Updating JavaScript references
If your existing code references the image by name, update it accordingly.
Before (relying on name):
<img src="logo.png" name="siteLogo" alt="Company logo">
<script>
var logo = document.images["siteLogo"];
logo.style.border = "2px solid blue";
</script>
After (using id):
<img src="logo.png" id="siteLogo" alt="Company logo">
<script>
var logo = document.getElementById("siteLogo");
logo.style.border = "2px solid blue";
</script>
Multiple images that previously shared a name
Since id values must be unique, you cannot give the same id to multiple elements. If you previously used the same name on several images and selected them as a group, switch to a shared class instead:
<img src="slide1.jpg" class="gallery-image" alt="Mountain landscape">
<img src="slide2.jpg" class="gallery-image" alt="Forest trail">
<img src="slide3.jpg" class="gallery-image" alt="River valley">
<script>
var images = document.querySelectorAll(".gallery-image");
images.forEach(function(img) {
img.style.borderRadius = "8px";
});
</script>
This approach is standards-compliant and gives you flexible, modern element selection using class for groups and id for unique elements.
The name attribute was historically used on <option> elements in older HTML specifications, but it has been obsolete since HTML5. The WHATWG HTML Living Standard does not list name as a valid attribute for <option>. The valid attributes for <option> are disabled, label, selected, and value, in addition to the global attributes (such as id, class, style, etc.).
It's important to understand that the name attribute on <option> never served the same purpose as name on <input> or <select>. For form submission, the browser sends the name from the parent <select> element paired with the value of the selected <option>. Putting name on individual <option> elements has no effect on form data and can mislead developers into thinking it influences form behavior.
Removing the obsolete name attribute ensures your HTML is standards-compliant, avoids confusion for developers maintaining the code, and prevents potential issues with future browser behavior. If you need to reference a specific <option> in JavaScript or CSS, use the id global attribute instead.
Examples
Incorrect: using the obsolete name attribute
<select id="pet-select" name="pet">
<option value="">--Please choose an option--</option>
<option name="dog-option" value="dog">Dog</option>
<option name="cat-option" value="cat">Cat</option>
<option name="hamster-option" value="hamster">Hamster</option>
</select>
This triggers the validation error because name is not a valid attribute on <option>.
Correct: using id instead of name
If you need to uniquely identify each option (for example, to target them with JavaScript or CSS), use the id attribute:
<select id="pet-select" name="pet">
<option value="">--Please choose an option--</option>
<option id="dog-option" value="dog">Dog</option>
<option id="cat-option" value="cat">Cat</option>
<option id="hamster-option" value="hamster">Hamster</option>
</select>
Correct: simply removing name if no reference is needed
In most cases, you don't need to identify individual options at all. The value attribute is sufficient for form submission, and you can remove name entirely:
<select id="pet-select" name="pet">
<option value="">--Please choose an option--</option>
<option value="dog">Dog</option>
<option value="cat">Cat</option>
<option value="hamster">Hamster</option>
</select>
Note that the name attribute on the <select> element itself is perfectly valid and necessary — it defines the key used when the form data is submitted. The obsolete attribute warning applies only to name on <option> elements.
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
navas 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>
An element with a tabindex attribute is interactive and focusable by the user, which conflicts with role="none" (or its synonym role="presentation"), because that role tells assistive technologies to ignore the element's semantics.
When a browser encounters this conflict, it ignores the role="none" declaration and keeps the element's native semantics. The element remains in the tab order and is announced normally by screen readers. The role="none" has no effect, so the code is misleading.
The role="none" role strips an element of its implicit ARIA semantics. A <table role="none">, for example, is no longer announced as a table. But the HTML spec requires browsers to preserve semantics on any element that is focusable. An element with tabindex is focusable by definition, so the two attributes contradict each other.
To fix this, decide what the element should actually do. If the element should be focusable and interactive, remove role="none". If the element should have no semantics and no interaction, remove tabindex.
Example with the issue
<div role="none" tabindex="0">
Click me
</div>
The validator warns that role="none" is ignored here because tabindex="0" makes the element focusable.
Fixed examples
Remove role="none" if the element should remain focusable:
<div tabindex="0">
Click me
</div>
Or remove tabindex if the element should truly have no semantics and no keyboard interaction:
<div role="none">
Click me
</div>
The <o:p> element is a Microsoft Office namespace tag that has no meaning in HTML and is not part of any HTML specification.
When content is copied from Microsoft Word, Outlook, or other Office applications and pasted into an HTML document, the source often includes proprietary XML namespace elements like <o:p>, <o:OfficeDocumentSettings>, and similar tags. These belong to the urn:schemas-microsoft-com:office:office namespace and are only understood by Office applications. Browsers ignore them, but the W3C validator flags them as errors because they are not valid HTML elements.
The <o:p> tag specifically is used by Word to wrap paragraph content. It typically appears inside <p> elements and most often contains nothing or just a non-breaking space ( ). Removing it entirely has no effect on the rendered page.
If the <o:p> element wraps actual text content, replace it with a standard HTML element like <span> or simply keep the text without any wrapper. If it is empty or contains only , delete it.
HTML examples
Invalid: Office namespace element in HTML
<p>This is a paragraph.<o:p></o:p></p>
<p>
<o:p> </o:p>
</p>
Valid: Office elements removed
<p>This is a paragraph.</p>
When cleaning up Office-generated HTML, also look for other common namespace prefixes like <w:, <m:, and <v:, along with mso- prefixed CSS properties in style attributes. These are all Office artifacts and should be removed. Many text editors and CMS platforms have a "Paste as plain text" option that strips this markup automatically.
The accept-charset attribute on a <form> element only accepts "UTF-8" as its value. Any other character encoding will trigger a validation error.
Older HTML specifications allowed values like "ISO-8859-1", "Windows-1252", or space-separated lists of encodings. The HTML living standard changed this. Now, "UTF-8" is the sole permitted value. Browsers already default to submitting form data in UTF-8 when no accept-charset is specified, so the attribute is rarely needed at all.
If a form currently specifies a non-UTF-8 encoding, the fix is either to switch the value to "UTF-8" or to remove the attribute entirely. Removing it is usually the better choice, since the browser will use UTF-8 by default when the page itself is served as UTF-8 (which it should be).
HTML examples
Invalid usage
<form action="/search" accept-charset="ISO-8859-1">
<input type="text" name="q">
<button type="submit">Search</button>
</form>
Valid usage
Remove the attribute and let the browser default to UTF-8:
<form action="/search">
<input type="text" name="q">
<button type="submit">Search</button>
</form>
Or explicitly set it to "UTF-8":
<form action="/search" accept-charset="UTF-8">
<input type="text" name="q">
<button type="submit">Search</button>
</form>
The meta element's charset attribute only accepts utf-8 as its value in HTML5.
Older HTML versions and XHTML allowed character encodings like windows-1252, iso-8859-1, or shift_jis in the charset attribute. The HTML living standard restricts this to utf-8 only. UTF-8 is the required character encoding for HTML5 documents, and the W3C validator enforces this rule.
If a document actually uses a different encoding, convert the file to UTF-8 using a text editor or command line tool, then update the meta tag accordingly. Most modern text editors can save files as UTF-8.
The <meta charset="utf-8"> declaration should appear as early as possible within the <head> element, ideally as the first child. It must appear within the first 1024 bytes of the document so that browsers can detect the encoding before parsing the rest of the content.
HTML examples
Invalid charset value
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="windows-1252">
<title>Example</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Valid charset value
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
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