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 requires every document to have a <title> element with at least one non-whitespace character. The title serves as the primary label for the page — it appears in the browser tab, in bookmarks, in search engine results, and is announced by screen readers when a user navigates to the page. An empty title leaves users with no way to identify or distinguish the page, which is especially problematic for accessibility. Screen reader users rely on the document title to understand what page they’ve landed on, and an empty title provides no context at all.
Browsers may attempt to display something (such as the URL) when the title is missing or empty, but this fallback is inconsistent and often produces a poor user experience. Search engines also depend on the <title> element for indexing and displaying results, so an empty title can negatively affect discoverability.
This error commonly occurs when templates or boilerplate code include a <title> tag as a placeholder that never gets filled in, or when content management systems fail to inject a title value into the template.
How to fix it
Add descriptive, concise text inside the <title> element that accurately reflects the content of the page. A good title is typically 20–70 characters long and specific enough to distinguish the page from other pages on the same site.
- Every page should have a unique title relevant to its content.
- Avoid generic titles like “Untitled” or “Page” — be specific.
- For multi-page sites, consider a format like “Page Name - Site Name”.
Examples
❌ Empty title element
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p>Welcome to our website.</p>
</body>
</html>
This triggers the error because the <title> element contains no text.
❌ Title with only whitespace
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> </title>
</head>
<body>
<p>Welcome to our website.</p>
</body>
</html>
This also triggers the error. Whitespace-only content is treated as empty.
✅ Title with descriptive text
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Getting Started - Automated Website Validator</title>
</head>
<body>
<p>Welcome to our website.</p>
</body>
</html>
The <title> element now contains meaningful text that identifies both the page and the site, making it accessible, SEO-friendly, and standards-compliant.
The id attribute uniquely identifies an element within a document, making it essential for fragment links, JavaScript targeting, CSS styling, and label associations. According to the HTML specification, an id value must contain at least one character and must not contain any ASCII whitespace. This means spaces, tabs, line feeds, carriage returns, and form feeds are all prohibited.
A common mistake is accidentally including a space in an id value, which effectively makes it look like multiple values — similar to how the class attribute works. However, unlike class, the id attribute does not support space-separated values. When a browser encounters an id with a space, behavior becomes unpredictable: some browsers may treat only the first word as the ID, while CSS and JavaScript selectors may fail entirely.
Why this matters
- Broken fragment links: A link like <a href="#my section"> won’t correctly scroll to an element with id="my section" in all browsers.
- JavaScript failures: document.getElementById("my section") may not return the expected element, and document.querySelector("#my section") will throw a syntax error because spaces are not valid in CSS ID selectors without escaping.
- CSS issues: A selector like #my section targets an element with id="my" that has a descendant <section> element — not what you intended.
- Accessibility: Screen readers and assistive technologies rely on id attributes for label associations (e.g., <label for="...">). A broken id can break form accessibility.
Best practices for id values
While the HTML specification technically allows any non-whitespace character in an id, it’s best practice to stick to ASCII letters, digits, hyphens (-), and underscores (_). Starting the value with a letter also ensures maximum compatibility with CSS selectors without needing escaping.
Examples
❌ Invalid: id contains a space
<div id="main content">
<p>Welcome to the page.</p>
</div>
The space between main and content makes this an invalid id.
✅ Fixed: Replace the space with a hyphen
<div id="main-content">
<p>Welcome to the page.</p>
</div>
✅ Fixed: Replace the space with an underscore
<div id="main_content">
<p>Welcome to the page.</p>
</div>
✅ Fixed: Use camelCase
<div id="mainContent">
<p>Welcome to the page.</p>
</div>
❌ Invalid: id with a space breaks label association
<label for="first name">First Name</label>
<input type="text" id="first name">
The for attribute won’t properly associate with the input, breaking accessibility.
✅ Fixed: Remove whitespace from both for and id
<label for="first-name">First Name</label>
<input type="text" id="first-name">
❌ Invalid: Trailing or leading whitespace
Whitespace isn’t always obvious. A trailing space or a copy-paste error can introduce invisible whitespace:
<section id="about ">
<h2>About Us</h2>
</section>
✅ Fixed: Trim all whitespace
<section id="about">
<h2>About Us</h2>
</section>
If you’re generating id values dynamically (e.g., from a CMS or database), make sure to trim and sanitize the values by stripping whitespace and replacing spaces with hyphens or underscores before rendering them into HTML.
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 <footer> element represents footer content for its nearest ancestor sectioning element or the <body>. It typically contains information like copyright notices, contact details, or links to related documents. The <header> element, on the other hand, represents introductory content or a group of navigational aids. The HTML living standard states that <header> must not appear as a descendant of <footer>, because embedding introductory content inside closing content creates a semantic contradiction.
It’s worth noting that neither <header> nor <footer> are sectioning content themselves—they are flow content with specific usage restrictions. The <footer> element’s content model explicitly excludes <header> descendants at any depth, meaning you can’t nest a <header> inside a <footer> even if there are other elements in between.
This restriction matters for several reasons:
- Semantics and accessibility: Screen readers and assistive technologies rely on the correct use of landmark elements. A <header> inside a <footer> sends conflicting signals about the purpose of that content, which can confuse users navigating by landmarks.
- Standards compliance: Violating the content model rules means your HTML is invalid, which can lead to unpredictable behavior across different browsers and parsing engines.
- Maintainability: Using elements according to their intended purpose makes your markup easier for other developers to understand and maintain.
Examples
❌ Invalid: <header> nested inside <footer>
<footer>
<header>
<h2>Contact Us</h2>
<nav>
<a href="/email">Email</a>
<a href="/phone">Phone</a>
</nav>
</header>
<p>© 2024 Example Corp.</p>
</footer>
This triggers the validation error because <header> is a direct child of <footer>.
❌ Invalid: <header> deeply nested inside <footer>
<footer>
<div class="footer-top">
<header>
<h3>Quick Links</h3>
</header>
</div>
<p>© 2024 Example Corp.</p>
</footer>
The restriction applies to any level of nesting, not just direct children. A <header> anywhere inside a <footer> is invalid.
✅ Valid: <header> and <footer> as siblings
If the content is truly introductory, it belongs outside the <footer>:
<header>
<h2>Contact Us</h2>
<nav>
<a href="/email">Email</a>
<a href="/phone">Phone</a>
</nav>
</header>
<footer>
<p>© 2024 Example Corp.</p>
</footer>
✅ Valid: Using headings directly inside <footer>
If you need a heading inside a footer, use heading elements (<h2>, <h3>, etc.) directly without wrapping them in a <header>:
<footer>
<h2>Contact Us</h2>
<nav>
<a href="/email">Email</a>
<a href="/phone">Phone</a>
</nav>
<p>© 2024 Example Corp.</p>
</footer>
✅ Valid: Using a <div> for grouping inside <footer>
If you need to group content visually within a footer, use a <div> instead of a <header>:
<footer>
<div class="footer-top">
<h3>Quick Links</h3>
<nav>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</div>
<div class="footer-bottom">
<p>© 2024 Example Corp.</p>
</div>
</footer>
✅ Valid: <header> inside an <article> within a <footer>
One exception worth noting: a <header> can appear inside a <footer> if it belongs to a new sectioning element like <article> or <section> nested within that footer. In this case, the <header> is a descendant of the <article>, not semantically of the <footer>:
<footer>
<article>
<header>
<h3>Latest Blog Post</h3>
</header>
<p>A summary of the latest post.</p>
</article>
<p>© 2024 Example Corp.</p>
</footer>
This is valid because the <header> serves as introductory content for the <article>, and sectioning elements reset the scope of <header> and <footer> restrictions.
The HTML living standard defines the content model of the <a> element as “transparent,” meaning it can contain whatever its parent element allows — with one critical exception: it must not contain any interactive content, which includes other <a> elements, <button>, <input>, <select>, <textarea>, and similar elements. This restriction exists at every level of nesting, not just direct children. If an <a> appears anywhere inside another <a>, even deeply nested within <div> or <span> elements, the markup is invalid.
Why This Is a Problem
Unpredictable browser behavior: When browsers encounter nested anchors, they don’t agree on how to handle them. Most browsers will attempt to “fix” the invalid markup by automatically closing the outer <a> before opening the inner one, but the resulting DOM structure may not match your intentions at all. This means your page layout, styling, and link targets can all break in unexpected ways.
Accessibility failures: Screen readers rely on the DOM tree to announce links to users. Nested anchors create ambiguous link boundaries — assistive technology may announce the wrong link text, skip links entirely, or confuse users about which link they’re activating. Keyboard navigation can also become unreliable, since tab order depends on a well-formed link structure.
Broken click targets: When links overlap, it’s unclear which link should activate when a user clicks the shared area. This results in a poor user experience where clicks may navigate to the wrong destination.
Common Causes
This error often arises in a few typical scenarios:
- Card components where the entire card is wrapped in an <a>, but individual elements inside (like a title or button) also need their own links.
- Navigation menus generated by CMS platforms or templating systems that accidentally produce nested link structures.
- Copy-paste mistakes where anchor tags get duplicated during content editing.
How to Fix It
The core fix is straightforward: ensure no <a> element exists as a descendant of another <a> element. Depending on your situation, you can:
- Separate the links so they are siblings rather than nested.
- Remove the outer link if only the inner link is needed.
- Use CSS and JavaScript for card-style patterns where the entire container needs to be clickable but inner links must remain independent.
Examples
Incorrect: Nested Anchors
<a href="/products">
Browse our <a href="/products/new">new arrivals</a> today
</a>
This is invalid because the inner <a> is a descendant of the outer <a>.
Fix: Separate the Links
<p>
<a href="/products">Browse our products</a> or check out our
<a href="/products/new">new arrivals</a> today.
</p>
Incorrect: Clickable Card with a Nested Link
<a href="/post/123" class="card">
<h2><a href="/post/123">Article Title</a></h2>
<p>A brief summary of the article content.</p>
</a>
Fix: Remove the Redundant Inner Link
If both links point to the same destination, simply remove the inner one:
<a href="/post/123" class="card">
<h2>Article Title</h2>
<p>A brief summary of the article content.</p>
</a>
Fix: Card with Multiple Distinct Links
When a card needs an overall clickable area and independent inner links, avoid wrapping everything in an <a>. Instead, use CSS positioning to stretch the primary link over the card:
<div class="card">
<h2><a href="/post/123" class="card-link">Article Title</a></h2>
<p>A brief summary of the article content.</p>
<p>Published by <a href="/author/jane">Jane Doe</a></p>
</div>
.card {
position: relative;
}
.card-link::after {
content: "";
position: absolute;
inset: 0;
}
.card a:not(.card-link) {
position: relative;
z-index: 1;
}
This approach makes the entire card clickable via the stretched ::after pseudo-element on the primary link, while the author link remains independently clickable above it — all without nesting any <a> elements.
Incorrect: Deeply Nested Anchor
The restriction applies at any depth, not just direct children:
<a href="/page">
<div>
<span>
<a href="/other">Nested link</a>
</span>
</div>
</a>
Fix: Restructure to Avoid Nesting
<div>
<a href="/page">Main page link</a>
<span>
<a href="/other">Other link</a>
</span>
</div>
Always verify that your final markup contains no <a> element inside another <a>, regardless of how many elements sit between them. Running your HTML through the W3C validator after making changes will confirm the issue is resolved.
The <header> element has a specific content model restriction: it must not contain <header>, <footer>, or <main> elements as descendants. This means that not only direct children but any nested <header> — even one buried several levels deep inside other elements — will trigger this validation error.
This restriction exists because each <header> is supposed to introduce its surrounding sectioning content (like <article>, <section>, <nav>, or the <body> itself). If one <header> contains another, it becomes unclear which section each header is introducing. This ambiguity hurts accessibility, as screen readers rely on these landmarks to help users navigate the page structure. Assistive technologies may announce nested headers incorrectly or confuse users about where sections begin and end.
A common scenario that triggers this error is when a site-wide header wraps a component (like a card or widget) that has its own <header>. Another frequent mistake is accidentally duplicating <header> tags when copying markup or working with template partials.
To fix this issue, you have a few options:
- Move the inner <header> outside the outer one entirely.
- Place the inner <header> inside a sectioning element like <section> or <article> that is itself inside the outer <header> — though this is unusual and likely a sign of a structural problem.
- Replace the inner <header> with a non-landmark element like <div> if it doesn’t truly represent introductory content for a section.
Examples
Incorrect: nested <header> elements
This markup nests a <header> for a featured article directly inside the page’s <header>, which is invalid:
<header>
<h1>Welcome to Our Shop</h1>
<header>
<h2>Featured Product</h2>
<p>Check out our latest arrival!</p>
</header>
</header>
Correct: use a <div> for the inner grouping
If the inner content is simply a visual grouping within the same header, replace the nested <header> with a <div>:
<header>
<h1>Welcome to Our Shop</h1>
<div>
<h2>Featured Product</h2>
<p>Check out our latest arrival!</p>
</div>
</header>
Correct: move the inner <header> into its own sectioning element
If the inner <header> truly introduces a distinct section of content, move it into an <article> or <section> outside the page header:
<header>
<h1>Welcome to Our Shop</h1>
<nav>
<ul>
<li><a href="/toys">Toys</a></li>
<li><a href="/books">Books</a></li>
<li><a href="/shoes">Shoes</a></li>
</ul>
</nav>
</header>
<article>
<header>
<h2>Featured Product</h2>
<p>Check out our latest arrival!</p>
</header>
<p>Product details go here.</p>
</article>
Correct: deeply nested case with a sectioning element in between
A <header> can appear inside a sectioning element that is itself inside another <header>, because the inner <header> is no longer a descendant of the outer <header> in terms of the content model — wait, actually it still is a descendant. The spec says the <header> must not appear as a descendant at any depth. So the only valid fix is to ensure no <header> exists anywhere inside another <header>:
<!-- Invalid: even with an article in between, it's still nested -->
<header>
<article>
<header>
<h2>News</h2>
</header>
</article>
</header>
<!-- Valid: move the article outside the header -->
<header>
<h1>My Site</h1>
</header>
<article>
<header>
<h2>News</h2>
</header>
<p>Article content here.</p>
</article>
The key takeaway is straightforward: a <header> should never contain another <header>, regardless of how many elements sit between them. Restructure your HTML so each <header> lives in its own sectioning context, and your document will be valid, accessible, and semantically clear.
The HTML specification defines <label> as an element whose content model allows phrasing content but explicitly excludes other <label> elements. When you nest one <label> inside another, browsers cannot determine which form control each label is meant to describe. This breaks the fundamental purpose of the <label> element—providing a clear, one-to-one association between a text description and its corresponding form control.
This issue matters for several reasons:
- Accessibility: Screen readers rely on the <label> element to announce the purpose of form controls. Nested labels create confusion about which label text belongs to which input, making forms difficult or impossible to navigate for users of assistive technology.
- Usability: Clicking a <label> should focus or activate its associated control. Nested labels create overlapping click targets with unpredictable behavior.
- Standards compliance: The WHATWG HTML living standard explicitly states that <label> elements must not be nested, and validators will flag this as an error.
This error commonly occurs in a few situations: accidentally duplicating closing tags, wrapping a complex form group in a <label> when a <fieldset> would be more appropriate, or using a templating system that inadvertently produces nested labels.
Examples
❌ Nested labels (invalid)
<label>
Full Name
<label>
First Name
<input type="text" name="first-name">
</label>
</label>
❌ Extra closing tag causing a parser issue
A stray closing </label> tag can sometimes cause the browser’s error recovery to produce unexpected nesting:
<label>Name</label></label>
<label for="email">Email</label>
While the extra </label> is the root problem here, some parsers and validators may interpret this as a nesting issue. Always ensure your opening and closing tags are properly matched.
✅ Separate labels for separate inputs
<label for="first-name">First Name</label>
<input type="text" id="first-name" name="first-name">
<label for="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name">
✅ Using implicit label association (one label per input)
<label>
First Name
<input type="text" name="first-name">
</label>
<label>
Last Name
<input type="text" name="last-name">
</label>
✅ Grouping related controls with <fieldset> instead of nesting labels
If you need to group multiple labeled inputs under a shared heading, use a <fieldset> with a <legend> instead of wrapping labels inside a label:
<fieldset>
<legend>Full Name</legend>
<label for="first-name">First Name</label>
<input type="text" id="first-name" name="first-name">
<label for="last-name">Last Name</label>
<input type="text" id="last-name" name="last-name">
</fieldset>
This approach provides the grouping semantics you need while keeping each <label> correctly associated with a single form control. The <legend> serves as the group-level description, and each <label> describes its individual input—giving both sighted users and assistive technology users a clear understanding of the form structure.
The <a> element is classified as interactive content, and the HTML spec explicitly states that interactive content must not be nested inside other interactive content. A <textarea> is a form control that accepts user input—clicking, focusing, typing, and selecting text within it. When it’s wrapped in a link, the browser faces a conflict: should a click focus the textarea or follow the link? Different browsers may resolve this differently, leading to inconsistent behavior.
Beyond browser inconsistency, this nesting creates serious accessibility problems. Screen readers and other assistive technologies rely on a clear, predictable document structure. When a form control is buried inside a link, the roles and interaction models overlap, making it confusing or even impossible for users relying on keyboard navigation or screen readers to interact with either element properly.
The fix depends on what you’re trying to achieve. If the <textarea> and the link are logically separate, simply move them to be siblings rather than nesting one inside the other. If you need them to appear visually grouped, use a wrapper <div> or another non-interactive container element instead.
Examples
❌ Invalid: <textarea> inside an <a> element
<a href="/comments">
<textarea name="comment" rows="4" cols="40"></textarea>
</a>
This triggers the validation error because the <textarea> is a descendant of the <a> element.
✅ Valid: <textarea> and <a> as siblings
<div>
<textarea name="comment" rows="4" cols="40"></textarea>
<a href="/comments">View all comments</a>
</div>
Here, both elements live side by side inside a neutral <div>, avoiding any nesting conflict.
✅ Valid: <textarea> inside a <form> with a separate link
<form action="/submit-comment" method="post">
<label for="comment">Your comment:</label>
<textarea id="comment" name="comment" rows="4" cols="40"></textarea>
<button type="submit">Submit</button>
</form>
<a href="/comments">View all comments</a>
This is the most semantically correct approach when the textarea is part of a form—keep the form controls in a <form> and place any navigation links outside of it.
Other interactive elements to watch for
The same rule applies to other interactive content inside <a> elements. You also cannot nest <button>, <input>, <select>, <details>, or another <a> inside a link. If the validator reports a similar error for any of these elements, the fix follows the same principle: move the interactive element out of the anchor.
The <a> element is classified as interactive content, meaning it expects user interaction (clicking to navigate). The <label> element is also interactive — clicking a label activates or focuses its associated form control. When a <label> is nested inside an <a>, the browser faces an ambiguous situation: should a click navigate to the link’s URL, or should it focus/activate the associated form control? The HTML specification resolves this by simply disallowing the nesting entirely.
According to the WHATWG HTML Living Standard, the content model of the <a> element is “transparent” but must not contain any interactive content. Since <label> is interactive content, it is not permitted as a descendant of <a> at any depth.
Beyond being invalid HTML, this nesting causes real problems:
- Accessibility: Screen readers may announce conflicting roles, confusing users who rely on assistive technology. The purpose of the element becomes unclear — is it a link or a form label?
- Unpredictable behavior: Different browsers may handle the click event differently, leading to inconsistent user experiences.
- Broken form association: The <label>‘s for attribute may not work as intended when the label is trapped inside a link.
The fix is straightforward: if you only need to style text inside a link, use a <span> or another non-interactive element instead of <label>. If you genuinely need both a link and a label, they should be separate, sibling elements rather than nested.
Examples
❌ Invalid: <label> inside <a>
<a href="/settings">
<label>Account Settings</label>
</a>
This triggers the validation error because <label> is interactive content nested inside <a>.
✅ Fixed: Replace <label> with <span>
<a href="/settings">
<span>Account Settings</span>
</a>
If the <label> was only used for styling purposes, a <span> with a CSS class achieves the same visual result without violating the specification.
❌ Invalid: <label> deeply nested inside <a>
<a href="/profile">
<div>
<label for="username">Edit Username</label>
</div>
</a>
The rule applies to all descendants, not just direct children. This is still invalid.
✅ Fixed: Separate the link and label
<label for="username">Edit Username</label>
<a href="/profile">View Profile</a>
When you need both a functional label and a link, keep them as siblings rather than nesting one inside the other.
✅ Fixed: Using <span> with a class for styling
<a href="/dashboard">
<span class="label-style">Dashboard</span>
</a>
.label-style {
font-weight: bold;
text-transform: uppercase;
}
This preserves any visual styling you need while keeping the HTML valid and the interaction model unambiguous.
The dt element represents a term or name in a description list (dl). According to the HTML specification, its content model is restricted to phrasing content, which means it can only contain text-level elements. Heading elements (h1 through h6) are flow content, not phrasing content, so nesting them inside a dt is invalid HTML.
This restriction exists because dt is designed to label or name something, while headings define the structural outline of a document. Mixing the two creates conflicting semantics — screen readers and other assistive technologies may misinterpret the document’s heading hierarchy, leading to a confusing experience for users navigating by headings. Browsers may also handle the invalid nesting inconsistently, potentially breaking the layout or the logical structure of the description list.
This issue commonly arises when developers want to visually style a definition term as a heading. The correct approach is to either restructure the markup so the heading sits outside the dt, or to style the dt directly with CSS to achieve the desired visual appearance without misusing heading elements.
How to fix it
You have several options:
- Move the heading before the description list. If the heading introduces a group of terms, place it above the dl element.
- Place the heading inside a dd element instead. The dd element accepts flow content, so headings are valid there.
- Style the dt with CSS. If you only need the term to look like a heading, apply font size, weight, and other styles directly to the dt without wrapping its content in a heading element.
Examples
❌ Invalid: heading inside a dt
<dl>
<dt>
<h2>API Reference</h2>
</dt>
<dd>Documentation for the public API.</dd>
</dl>
The h2 is a descendant of dt, which violates the content model.
✅ Valid: heading placed before the description list
<h2>API Reference</h2>
<dl>
<dt>Endpoint</dt>
<dd>The URL used to access the API.</dd>
</dl>
When the heading introduces the entire list, placing it before the dl is the cleanest solution.
✅ Valid: heading inside a dd element
<dl>
<dt>API Reference</dt>
<dd>
<h2>Overview</h2>
<p>Documentation for the public API.</p>
</dd>
</dl>
The dd element accepts flow content, so headings are permitted there.
✅ Valid: styling the dt to look like a heading
<style>
.term-heading {
font-size: 1.5em;
font-weight: bold;
}
</style>
<dl>
<dt class="term-heading">API Reference</dt>
<dd>Documentation for the public API.</dd>
</dl>
This approach gives you the visual appearance of a heading while keeping the markup valid. Keep in mind that styled dt elements won’t appear in the document’s heading outline, so only use this when a true heading isn’t semantically needed.
✅ Valid: using a span for inline styling inside dt
<dl>
<dt><span class="term-heading">API Reference</span></dt>
<dd>Documentation for the public API.</dd>
</dl>
Since span is phrasing content, it’s perfectly valid inside dt and gives you a styling hook without breaking the content model.
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 th element is specifically designed to represent a header cell in a table. It inherently conveys header semantics to browsers, screen readers, and other assistive technologies. When you place a heading element like h3 inside a th, you’re creating a structural conflict — the content is simultaneously acting as a table header and a document section heading. The HTML specification restricts the content model of th to “flow content, but with no header, footer, sectioning content, or heading content descendants.”
This matters for several reasons:
- Accessibility: Screen readers use heading elements to build a document outline and allow users to navigate between sections. A heading buried inside a table header cell disrupts this navigation, creating confusion about the page structure. The th element already communicates its role as a header through the table’s own semantics.
- Document structure: Headings define the hierarchical structure of a document. Placing them inside table cells implies that a new document section begins within the table, which is almost never the intended meaning.
- Standards compliance: Browsers may handle this invalid nesting inconsistently, leading to unpredictable rendering or accessibility tree representations.
The fix is straightforward: remove the heading element from inside the th. If the text inside the th needs to be visually larger or bolder, apply CSS styles directly to the th element or use a span with a class. If the heading was meant to title the entire table, move it outside the table or use the caption element.
Examples
❌ Incorrect: heading inside th
<table>
<tr>
<th>Month</th>
<th><h3>Revenue</h3></th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
The h3 inside the second th triggers the validation error.
✅ Fixed: remove the heading, use plain text
<table>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
The th element already communicates that “Revenue” is a header. No heading element is needed.
✅ Fixed: use CSS for visual styling
If the heading was used to make the text look bigger or styled differently, apply CSS to the th instead:
<table>
<tr>
<th>Month</th>
<th class="prominent">Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
<style>
.prominent {
font-size: 1.2em;
font-weight: bold;
}
</style>
✅ Fixed: use caption for a table title
If the heading was meant to describe the entire table, use the caption element:
<table>
<caption>Monthly Revenue</caption>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
The caption element is the semantically correct way to provide a title or description for a table. You can style it with CSS to match the appearance of a heading. If you still need a heading in the document outline to precede the table, place it before the table element:
<h3>Revenue Report</h3>
<table>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
This approach keeps the document structure clean while maintaining proper table semantics. The same rule applies to all heading levels — h1, h2, h3, h4, h5, and h6 are all equally invalid inside th (and td) elements.
The <th> element already carries semantic meaning as a table header cell. Placing a heading element like <h6> inside it creates a conflict in the document’s semantic structure. Screen readers and other assistive technologies use headings to build a navigable outline of the page, and they also interpret <th> elements as table headers. Nesting one inside the other produces a confusing, redundant structure that can mislead assistive technologies about the page’s organization and the table’s meaning.
According to the WHATWG HTML living standard, the content model for <th> is “flow content, but with no header, footer, sectioning content, or heading content descendants.” This means <h1>, <h2>, <h3>, <h4>, <h5>, and <h6> are all explicitly disallowed inside <th>.
People commonly make this mistake when trying to visually style table header text to look bolder or larger. Since <th> cells are already rendered bold by default in most browsers, and CSS gives you full control over font size, weight, and appearance, there’s no need to use heading elements for visual styling inside table headers.
How to Fix It
- Remove the heading element from inside the <th> and place the text directly inside the <th>.
- Use CSS if you need the table header text to appear larger or styled differently.
- Use a <caption> element if the heading was meant to serve as a title for the table, or place a heading element before the <table>.
Examples
❌ Incorrect: Heading inside <th>
<table>
<tr>
<th><h6>Product</h6></th>
<th><h6>Price</h6></th>
</tr>
<tr>
<td>Widget</td>
<td>$19.99</td>
</tr>
</table>
This triggers the validation error because <h6> elements are descendants of <th> elements.
✅ Correct: Plain text inside <th>
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$19.99</td>
</tr>
</table>
The <th> element is already semantically a header, so no additional heading tag is needed.
✅ Correct: Styled <th> with CSS
If you need the header cells to have a specific visual appearance, use CSS:
<style>
.table-header {
font-size: 0.85rem;
text-transform: uppercase;
letter-spacing: 0.05em;
}
</style>
<table>
<tr>
<th class="table-header">Product</th>
<th class="table-header">Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$19.99</td>
</tr>
</table>
✅ Correct: Using <caption> for a table title
If the heading was intended as a title for the entire table, use the <caption> element instead:
<table>
<caption>Monthly Revenue</caption>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
The <caption> element is the semantically correct way to provide a title for a table. It is announced by screen readers and associated directly with the table, giving users proper context. You can also place a heading before the <table> element if a <caption> doesn’t suit your layout needs.
The HTML specification defines the <button> element’s content model as “phrasing content” but explicitly excludes interactive content. Since the <a> element (when it has an href attribute) is classified as interactive content, nesting it inside a <button> violates this rule. The same restriction applies to any element that has role="button", as it semantically functions as a button.
This is problematic for several reasons:
- Accessibility: Screen readers and assistive technologies cannot reliably convey the purpose of nested interactive elements. A user tabbing through the page may encounter confusing or duplicate focus targets, and the intended action becomes ambiguous.
- Unpredictable behavior: Browsers handle nested interactive elements inconsistently. Clicking the link inside a button might trigger the button’s click handler, the link’s navigation, both, or neither — depending on the browser.
- Standards compliance: The HTML specification forbids this nesting to ensure a clear, unambiguous interaction model for all users and user agents.
To fix this, decide what the element should do. If it navigates to a URL, use an <a> element (styled as a button if needed). If it performs an action like submitting a form or toggling something, use a <button>. If you need both behaviors, handle navigation programmatically via JavaScript on a <button>, or separate them into two distinct elements.
Examples
❌ Incorrect: link nested inside a button
<button>
<a href="/dashboard">Go to Dashboard</a>
</button>
❌ Incorrect: link inside an element with role="button"
<div role="button">
<a href="/settings">Settings</a>
</div>
✅ Correct: use an anchor styled as a button
If the goal is navigation, use an <a> element and style it to look like a button with CSS:
<a href="/dashboard" class="button">Go to Dashboard</a>
.button {
display: inline-block;
padding: 8px 16px;
background-color: #007bff;
color: #fff;
text-decoration: none;
border-radius: 4px;
cursor: pointer;
}
✅ Correct: use a button with JavaScript navigation
If you need button semantics but also want to navigate, handle the navigation in JavaScript:
<button type="button" onclick="location.href='/dashboard'">
Go to Dashboard
</button>
✅ Correct: separate the two elements
If both a button action and a link are genuinely needed, place them side by side:
<button type="button">Save</button>
<a href="/dashboard">Go to Dashboard</a>
✅ Correct: link without href inside a button (edge case)
An <a> element without an href attribute is not interactive content, so it is technically valid inside a <button>. However, this is rarely useful in practice:
<button type="button">
<a>Label text</a>
</button>
As a general rule, never nest one clickable element inside another. This applies not only to <a> inside <button>, but also to other combinations like <button> inside <a>, or <a> inside <a>. Keeping interactive elements separate ensures predictable behavior and a good experience for all users.
The <label> element serves a specific purpose in HTML: it represents a caption for a form control. It can be associated with a control either through the for attribute (referencing the control’s id) or by wrapping the form control inside the <label> itself. Placing a <label> inside a <button> is semantically incorrect because a button is not a form control that benefits from labeling in this way — the button’s own text content or aria-label attribute already serves as its accessible name.
The HTML specification defines the content model of <button> as phrasing content, but explicitly excludes interactive content. Since <label> is classified as interactive content (it directs focus to its associated control when clicked), nesting it inside a <button> creates ambiguous behavior. When a user clicks the label, should it activate the button, or should it shift focus to the label’s associated control? Browsers handle this conflict inconsistently, which leads to unpredictable user experiences.
From an accessibility standpoint, this nesting is problematic because screen readers may announce the button in a confusing way, potentially reading it as containing a label for another element. The button’s accessible name should come directly from its text content, not from a nested <label>.
To fix this issue, simply replace the <label> with a <span> or plain text inside the button. If you need to style part of the button’s text differently, <span> elements are perfectly valid inside buttons.
Examples
❌ Invalid: <label> inside a <button>
<button type="submit">
<label>Submit Form</label>
</button>
❌ Invalid: <label> with a for attribute inside a <button>
<button type="button">
<label for="file-input">Choose File</label>
</button>
<input type="file" id="file-input">
✅ Fixed: Use plain text inside the <button>
<button type="submit">
Submit Form
</button>
✅ Fixed: Use a <span> for styling purposes
<button type="submit">
<span class="button-text">Submit Form</span>
</button>
✅ Fixed: Separate the <label> and <button>
If you need a label to describe a button’s purpose in a form, place the <label> outside the button and use the for attribute to associate it with a related input, or use aria-label on the button itself:
<label for="username">Username</label>
<input type="text" id="username">
<button type="submit" aria-label="Submit the username form">
Submit
</button>
The <button> element has a strict content model defined by the WHATWG HTML Living Standard: it accepts phrasing content, but with the explicit exclusion of interactive content. The <input> element is classified as interactive content, which means nesting it inside a <button> produces invalid HTML and triggers this W3C validator error.
Why This Is a Problem
Unpredictable browser behavior: When interactive elements are nested inside a <button>, browsers must figure out which element should receive user interactions like clicks, focus, and keyboard input. Different browsers handle this differently — some may ignore the inner <input> entirely, while others may produce confusing behavior where clicks are swallowed by the <button> before reaching the <input>.
Accessibility issues: Screen readers and other assistive technologies expect a <button> to contain descriptive text or simple phrasing content, not other form controls. Nesting an <input> inside a <button> creates a confusing and potentially unusable experience for users who rely on assistive technology. The relationship between the two controls becomes ambiguous — is the user interacting with the button or the input?
Standards compliance: Valid HTML is the foundation for consistent rendering and behavior across browsers and devices. Using invalid nesting can lead to subtle bugs that are difficult to diagnose, especially as browsers update their parsing behavior.
Other Elements You Cannot Nest Inside <button>
The restriction applies to all interactive content, not just <input>. You also cannot place these elements inside a <button>:
- <a> (with an href attribute)
- <button>
- <select>
- <textarea>
- <label>
- <audio> and <video> (with controls)
- <embed>, <iframe>, <object>
- Any element with a tabindex attribute
How to Fix It
The fix is straightforward: move the <input> out of the <button> so they are sibling elements. Wrap them in a <form>, <div>, or another suitable container, and use CSS to achieve any desired visual layout.
Examples
❌ Invalid: <input> nested inside <button>
<button>
Submit
<input type="text" name="example">
</button>
This triggers the error because <input> is interactive content placed inside a <button>.
✅ Fixed: <input> and <button> as siblings
<form>
<input type="text" name="example">
<button type="submit">Submit</button>
</form>
Both elements are direct children of the <form>, making the markup valid and the controls independently accessible.
❌ Invalid: Hidden <input> inside <button>
You might think a hidden input is okay since it’s not visually interactive, but <input type="hidden"> is still an <input> element and is still prohibited inside <button>:
<button type="submit">
Save
<input type="hidden" name="action" value="save">
</button>
✅ Fixed: Hidden <input> moved outside <button>
<form>
<input type="hidden" name="action" value="save">
<button type="submit">Save</button>
</form>
❌ Invalid: Checkbox inside a <button> for a toggle effect
<button class="toggle">
<input type="checkbox" name="darkmode"> Dark Mode
</button>
✅ Fixed: Use a <label> instead
If the intent is a clickable toggle, a <label> paired with a checkbox achieves the same visual result with valid, accessible markup:
<label class="toggle">
<input type="checkbox" name="darkmode"> Dark Mode
</label>
Alternatively, if you truly need a button that toggles state, use JavaScript with the aria-pressed attribute instead of embedding a checkbox:
<button type="button" class="toggle" aria-pressed="false">
Dark Mode
</button>
Keep <input> and <button> as separate, sibling elements. If you need them to appear visually grouped, use CSS for layout and styling rather than nesting one interactive element inside another.
The <footer> element represents a footer for its nearest ancestor sectioning content (such as <article>, <section>, <nav>, or <aside>) or sectioning root element (such as <body>, <blockquote>, or <details>). It typically contains information about its section, such as authorship, copyright data, links to related documents, or contact information.
According to the WHATWG HTML Living Standard, the content model for <footer> is “flow content, but with no <header>, <footer>, or <main> element descendants.” This means a <footer> must not appear anywhere inside another <footer>, regardless of how deeply nested it is. Even if there are other elements in between, the restriction still applies.
Why This Is a Problem
Standards compliance: Nesting <footer> elements violates the HTML specification and produces a validation error. This signals a structural issue in your document.
Semantic ambiguity: A <footer> is meant to describe metadata for its nearest sectioning ancestor. When one <footer> is nested inside another, it becomes unclear which section each footer is associated with. This undermines the semantic meaning of the element.
Accessibility: Screen readers and assistive technologies rely on the semantic structure of HTML to convey page organization to users. A nested <footer> can confuse these tools, potentially causing them to misrepresent or skip content, degrading the experience for users who depend on them.
Browser inconsistencies: While browsers are generally forgiving of invalid markup, they may handle nested <footer> elements differently, leading to unpredictable rendering or behavior.
How to Fix It
The most common fix depends on why the nesting occurred in the first place:
- If the inner <footer> is purely for styling purposes, replace it with a <div> and use a CSS class instead.
- If the inner <footer> belongs to a nested section, make sure it’s inside its own <article> or <section> element — not directly inside the outer <footer>.
- If the nesting is accidental, remove the inner <footer> entirely.
Examples
❌ Incorrect: Nested <footer> elements
<footer>
<p>© 2024 Example Corp.</p>
<footer>
<p>Built with love by the web team.</p>
</footer>
</footer>
This is invalid because a <footer> appears as a descendant of another <footer>.
✅ Fixed: Replace inner <footer> with a <div>
<footer>
<p>© 2024 Example Corp.</p>
<div class="footer-credits">
<p>Built with love by the web team.</p>
</div>
</footer>
❌ Incorrect: Deeply nested <footer> inside another <footer>
The restriction applies at any depth, not just direct children:
<footer>
<div class="wrapper">
<article>
<footer>
<p>Article author info</p>
</footer>
</article>
</div>
</footer>
Even though there are intermediate elements, the inner <footer> is still a descendant of the outer <footer>, which is not allowed.
✅ Fixed: Move the article outside the <footer>
<article>
<footer>
<p>Article author info</p>
</footer>
</article>
<footer>
<div class="wrapper">
<p>© 2024 Example Corp.</p>
</div>
</footer>
✅ Fixed: Each <footer> belongs to its own section
It’s perfectly valid to have multiple <footer> elements on a page, as long as they aren’t nested inside each other:
<article>
<h2>Blog Post Title</h2>
<p>Post content goes here.</p>
<footer>
<p>Written by Jane Doe on January 1, 2024</p>
</footer>
</article>
<footer>
<p>© 2024 Example Corp. All rights reserved.</p>
</footer>
Each <footer> here is associated with its nearest sectioning ancestor — the first with the <article>, the second with the <body> — and neither is nested inside the other.
The th element has a specific role in HTML: it defines a header cell within a table. It already carries implicit heading semantics through its association with the rows or columns it describes. When you place an h1–h6 element inside a th, you’re nesting one type of heading structure inside another, which violates the HTML content model. The HTML specification explicitly excludes heading elements from the allowed content of th.
This causes several problems:
- Document outline confusion: Heading elements contribute to the document’s outline and sectioning structure. Placing them inside table headers injects unexpected entries into the outline that don’t represent actual document sections, making navigation unpredictable.
- Accessibility issues: Screen readers treat headings and table headers differently. A heading inside a th creates conflicting signals—assistive technology may announce the content as both a table header and a document heading, confusing users who rely on either navigation method.
- Standards compliance: Browsers may handle this invalid nesting inconsistently, leading to unpredictable rendering or behavior across different environments.
If your goal is to make the text inside a th visually larger or bolder, use CSS instead. The th element is already rendered bold by default in most browsers, and you can further style it with font-size, font-weight, or any other CSS property.
Examples
Incorrect: heading inside a th element
<table>
<tr>
<th><h1>Product</h1></th>
<th><h1>Price</h1></th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
This triggers the validation error because h1 elements are nested inside th elements.
Fixed: plain text in th, heading moved outside the table
<h1>Product Pricing</h1>
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
The heading now introduces the table as a whole, and the th elements contain plain text.
Fixed: styling th with CSS instead of using headings
If you want the table headers to have a specific visual appearance, apply CSS directly to the th elements:
<style>
.styled-table th {
font-size: 1.5rem;
font-weight: bold;
text-transform: uppercase;
}
</style>
<table class="styled-table">
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
Fixed: using caption for a table title
If the heading was meant to serve as a title for the table, the caption element is the semantically correct choice:
<table>
<caption>Product Pricing</caption>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
The caption element is specifically designed to label a table and is properly associated with it for assistive technology. You can style it with CSS to achieve any visual appearance you need.
The HTML living standard defines both <a> and <button> as interactive content. Interactive content elements cannot be descendants of other interactive content elements. When you place a <button> inside an <a>, you create an ambiguous situation: should a click activate the link navigation or the button action? Browsers handle this inconsistently, which leads to unpredictable behavior for all users.
This is especially problematic for accessibility. Screen readers and other assistive technologies rely on a clear, well-defined element hierarchy to communicate the purpose of controls to users. A button nested inside a link creates a confusing experience — the user may hear both a link and a button announced, with no clear indication of what will actually happen when they activate it. Keyboard navigation can also break, as focus behavior becomes unreliable.
The same rule applies to elements that aren’t literally <button> but carry role="button". For example, a <span role="button"> inside an <a> tag triggers the same validation error, because the ARIA role makes it semantically interactive.
How to Fix It
The fix depends on what you’re trying to achieve:
- If the element should navigate to a URL, use an <a> element and style it to look like a button with CSS. Remove the <button> entirely.
- If the element should perform a JavaScript action, use a <button> element and remove the wrapping <a>. Attach the action via an event listener.
- If you need both a link and a button, place them side by side as siblings rather than nesting one inside the other.
Examples
❌ Invalid: Button inside a link
<a href="/dashboard">
<button>Go to Dashboard</button>
</a>
✅ Fixed: Link styled as a button
If the goal is navigation, use a link and style it with CSS:
<a href="/dashboard" class="btn">Go to Dashboard</a>
.btn {
display: inline-block;
padding: 8px 16px;
background-color: #007bff;
color: #fff;
text-decoration: none;
border-radius: 4px;
}
✅ Fixed: Button with a JavaScript action
If the goal is to trigger an action (like navigating programmatically), use a button on its own:
<button type="button" onclick="window.location.href='/dashboard'">
Go to Dashboard
</button>
❌ Invalid: Element with role="button" inside a link
<a href="/settings">
<span role="button">Settings</span>
</a>
✅ Fixed: Remove the redundant role
Since the <a> element already communicates interactivity, the inner role="button" is unnecessary and conflicting. Simply use the link directly:
<a href="/settings">Settings</a>
❌ Invalid: Link inside a button
Note that the reverse — an <a> inside a <button> — is also invalid for the same reason:
<button>
<a href="/home">Home</a>
</button>
✅ Fixed: Choose one element
<a href="/home" class="btn">Home</a>
The key principle is simple: never nest one interactive element inside another. Pick the element that best matches the semantics of your use case — <a> for navigation, <button> for actions — and use CSS to achieve the visual design you need.
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 <header> element represents introductory content for its nearest ancestor sectioning content or sectioning root element. It typically contains headings, logos, navigation, and search forms. The <footer> element represents a footer for its nearest ancestor sectioning content or sectioning root element, typically containing information like authorship, copyright data, or links to related documents.
The HTML specification states that <header> must not contain <header> or <footer> descendants. This restriction exists because these elements carry specific semantic meaning. A <footer> nested inside a <header> creates a contradictory document structure — it would simultaneously represent introductory content (by being in the header) and concluding/supplementary content (by being a footer). This confuses assistive technologies like screen readers, which use these landmark elements to help users navigate the page. When a screen reader encounters a <footer> inside a <header>, it cannot accurately convey the document structure to the user.
Note that this rule applies regardless of how deeply nested the <footer> is. Even if the <footer> is inside a <div> that is inside the <header>, it still violates the specification because it is a descendant of the <header>.
How to fix it
- Move the <footer> outside the <header> — Place it as a sibling element after the <header> closes.
- Replace <footer> with a non-semantic element — If you only need a visual container within the header (not actual footer semantics), use a <div> or <p> instead.
- Use a sectioning element as a boundary — If you genuinely need footer-like content within the header area, wrap it in a sectioning element like <section> or <article>. Because <footer> applies to its nearest sectioning ancestor, placing it inside a <section> within the <header> would technically satisfy the spec — but this approach should only be used when it makes semantic sense.
Examples
❌ Incorrect: <footer> nested inside <header>
<header>
<h1>My Website</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<footer>
<p>© 2024 My Website</p>
</footer>
</header>
❌ Incorrect: deeply nested <footer> still inside <header>
<header>
<h1>My Website</h1>
<div class="header-bottom">
<footer>
<p>Contact us at info@example.com</p>
</footer>
</div>
</header>
✅ Correct: <footer> moved outside <header>
<header>
<h1>My Website</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<footer>
<p>© 2024 My Website</p>
</footer>
✅ Correct: using a <div> for non-semantic content inside the header
<header>
<h1>My Website</h1>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<div class="header-meta">
<p>Contact us at info@example.com</p>
</div>
</header>
✅ Correct: <footer> inside a sectioning element within the header
<header>
<h1>Latest News</h1>
<article>
<h2>Featured Story</h2>
<p>A brief summary of the story...</p>
<footer>
<p>By Jane Doe, June 2024</p>
</footer>
</article>
</header>
In this last example, the <footer> is a descendant of the <article> element (a sectioning content element), so it acts as the footer for the article rather than for the <header>. This is valid because the spec forbids <footer> as a descendant of <header> only when there is no intervening sectioning content element.
The th element is specifically designed to act as a header cell within a table. It already carries implicit heading semantics — screen readers announce th content as a header when navigating table cells. When you place an h5 (or any h1–h6) inside a th, you’re creating a conflict: the content is simultaneously a table header and a document section heading. This breaks the document’s outline structure and creates confusing behavior for assistive technologies, which may announce the content as both a table header and a section heading.
The HTML specification restricts the content model of th to “flow content, but with no header, footer, sectioning content, or heading content descendants.” Heading elements (h1 through h6) fall under heading content, so placing any of them inside a th is invalid.
This issue typically arises when developers want the text inside a th to look like a heading — larger, bolder, or styled differently. The correct approach is to use CSS to style the th content directly, rather than wrapping it in a heading element.
How to Fix It
- Remove the heading element from inside the th.
- Keep the text content directly inside the th.
- Use CSS to apply any desired visual styling to the th element.
- If the heading is meant to describe the entire table (not just a column), move it outside the table or use the <caption> element.
Examples
❌ Incorrect: Heading inside a th
<table>
<tr>
<th><h5>Product</h5></th>
<th><h5>Price</h5></th>
</tr>
<tr>
<td>Widget</td>
<td>$9.99</td>
</tr>
</table>
This triggers the validation error because h5 elements are nested inside th elements.
✅ Correct: Plain text inside th, styled with CSS
<table>
<tr>
<th class="table-heading">Product</th>
<th class="table-heading">Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$9.99</td>
</tr>
</table>
<style>
.table-heading {
font-size: 1.1em;
font-weight: bold;
text-transform: uppercase;
}
</style>
The th elements already convey header semantics. CSS handles the visual presentation without introducing invalid markup.
✅ Correct: Using <caption> for a table title
If the heading was meant to describe the table as a whole, use <caption> instead:
<table>
<caption>Monthly Revenue</caption>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
✅ Correct: Heading placed before the table
If you need a document-level heading that introduces the table, place it outside:
<h5>Revenue per Month</h5>
<table>
<tr>
<th>Month</th>
<th>Revenue</th>
</tr>
<tr>
<td>January</td>
<td>$500</td>
</tr>
</table>
This keeps the document outline clean and avoids nesting headings inside table cells. The same rule applies to all heading levels — h1, h2, h3, h4, h5, and h6 are all equally invalid inside th (and td) elements.
The HTML specification defines a strict content model for the th element: it accepts flow content, but specifically excludes header, footer, sectioning content, and heading content (h1–h6). This restriction exists because th elements are themselves headers — they describe the data in their corresponding row or column. Placing a heading element inside a th creates a conflict in the document outline and semantic structure.
This matters for several reasons:
- Accessibility: Screen readers use headings to build a navigable document outline. Headings buried inside table header cells can confuse assistive technology, making it harder for users to understand the page structure and navigate between sections.
- Document outline: Heading elements define the hierarchical structure of a document’s content. When headings appear inside table cells, they disrupt this hierarchy and create unexpected, often meaningless, sections in the outline.
- Standards compliance: Browsers may handle this invalid nesting inconsistently, and the W3C validator will flag it as an error.
A common reason developers place headings in th cells is to achieve a specific visual style — larger or bolder text. The correct approach is to use CSS to style the th content directly, keeping the markup clean and valid.
How to Fix It
- Remove the heading element from inside the th.
- Move the heading above the table if you need a title or section heading for the table.
- Use CSS to style the th text if you need a particular visual appearance.
- Use the caption element if you want to provide a visible title that is semantically associated with the table.
Examples
❌ Incorrect: Heading inside a th element
<table>
<tr>
<th><h2>Product</h2></th>
<th><h2>Price</h2></th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
This triggers the validation error because h2 elements are not permitted as descendants of th.
✅ Correct: Plain text inside th, heading moved outside
<h2>Product Pricing</h2>
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
✅ Correct: Using caption for the table title
<table>
<caption>Product Pricing</caption>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
The caption element is the semantically appropriate way to give a table a title. It is announced by screen readers in context with the table, providing a better experience than a heading placed before the table.
✅ Correct: Styling th with CSS for visual emphasis
If the heading was added purely for visual effect, use CSS instead:
<style>
.prominent-header th {
font-size: 1.5em;
font-weight: bold;
color: #333;
}
</style>
<table class="prominent-header">
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
This gives you full control over the appearance of header cells without breaking the document structure or introducing validation errors. Remember: th elements are already semantically headers, so there’s no need to wrap their content in heading elements.
The <caption> element is designed to be a brief, descriptive label for its parent <table>. According to the HTML specification, <caption> accepts flow content but explicitly forbids descendant <table> elements. This restriction exists because a table nested inside a caption creates a confusing and semantically meaningless structure — the caption is supposed to describe the table, not contain another one.
Why this is a problem
- Accessibility: Screen readers announce the <caption> as the title of the table. A nested table inside a caption creates a confusing experience for assistive technology users, as the relationship between the tables becomes ambiguous and the caption loses its descriptive purpose.
- Standards compliance: The WHATWG HTML living standard explicitly states that <caption> must have “no <table> element descendants.” Violating this produces a validation error.
- Rendering inconsistencies: Browsers may handle this invalid nesting differently, leading to broken or unpredictable layouts across different environments.
How to fix it
- Remove the table from the caption. The <caption> should contain only text and simple inline elements like <span>, <strong>, <em>, or <a>.
- Place the nested table outside the parent table, either before or after it, or restructure your layout so both tables are siblings.
- If the data in the nested table is genuinely related to the caption’s purpose, consider expressing it as plain text or using a different structural approach entirely.
Examples
❌ Incorrect: A table nested inside a caption
<table>
<caption>
Summary
<table>
<tr>
<td>Extra info</td>
<td>Details</td>
</tr>
</table>
</caption>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
<tr>
<td>Alice</td>
<td>95</td>
</tr>
</table>
This triggers the validation error because a <table> appears as a descendant of the <caption> element.
✅ Correct: Caption contains only text, tables are separate
<table>
<caption>Summary — Extra info: Details</caption>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
<tr>
<td>Alice</td>
<td>95</td>
</tr>
</table>
If the extra information truly requires its own table, place it as a sibling:
<table>
<caption>Summary</caption>
<tr>
<th>Name</th>
<th>Score</th>
</tr>
<tr>
<td>Alice</td>
<td>95</td>
</tr>
</table>
<table>
<caption>Additional details</caption>
<tr>
<td>Extra info</td>
<td>Details</td>
</tr>
</table>
✅ Correct: Caption with inline formatting only
<table>
<caption>
<strong>Quarterly Results</strong> — <em>All figures in USD</em>
</caption>
<tr>
<th>Quarter</th>
<th>Revenue</th>
</tr>
<tr>
<td>Q1</td>
<td>$1.2M</td>
</tr>
</table>
This is valid because the <caption> contains only text and inline elements (<strong>, <em>), with no <table> descendants.
The HTML living standard defines a content model for the <a> element that explicitly excludes interactive content from appearing as descendants. Interactive content includes elements like <button>, <input>, <select>, <textarea>, and other <a> elements. When you nest an <input> inside a link, browsers face an ambiguous situation: should a click activate the link or interact with the input? Different browsers may handle this differently, leading to inconsistent behavior.
This restriction also matters for accessibility. Screen readers and other assistive technologies rely on a clear, predictable DOM structure. Nesting interactive elements creates confusion for users navigating with keyboards or screen readers, as the focus order and interaction model become unclear. A user tabbing through the page might not understand that an input lives inside a link, or they might be unable to interact with one of the two elements.
Common scenarios where this issue arises include wrapping a search input in a link to make the entire area clickable, or placing a checkbox inside a link to combine selection with navigation. In all cases, the solution is to separate the interactive elements.
Examples
❌ Incorrect: <input> inside an <a> element
<a href="/search">
<input type="text" placeholder="Search...">
</a>
This triggers the validation error because <input> is interactive content nested inside <a>.
✅ Correct: Separate the elements
<form action="/search">
<input type="text" placeholder="Search...">
<button type="submit">Search</button>
</form>
If the goal is to navigate to a search page, use a <form> with an action attribute instead of wrapping the input in a link.
❌ Incorrect: Checkbox inside a link
<a href="/settings">
<input type="checkbox" id="notify"> Enable notifications
</a>
✅ Correct: Place the link and input as siblings
<label>
<input type="checkbox" id="notify"> Enable notifications
</label>
<a href="/settings">Go to settings</a>
✅ Correct: Use styling to achieve a clickable area
If you want a visually combined area where clicking navigates somewhere, avoid using an <input> altogether and style the link instead:
<a href="/search" class="search-link">
<span>Search...</span>
</a>
Alternatively, if you need both a link and an input near each other, use CSS layout to position them visually together while keeping them as separate elements in the markup:
<div class="search-bar">
<input type="text" placeholder="Search...">
<a href="/search">Go</a>
</div>
❌ Incorrect: Hidden input inside a link
Even hidden or non-visible inputs trigger this error:
<a href="/page">
<input type="hidden" name="ref" value="home">
Click here
</a>
✅ Correct: Move the hidden input outside the link
<input type="hidden" name="ref" value="home">
<a href="/page">Click here</a>
If the hidden input is meant to pass data during navigation, consider using query parameters in the link’s href instead:
<a href="/page?ref=home">Click here</a>
Ready to validate your sites?
Start your free trial today.