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 <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>
<divclass="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>
<divclass="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>
<divclass="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 <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>
<ahref="/">Home</a>
<ahref="/about">About</a>
</nav>
<footer>
<p>© 2024 My Website</p>
</footer>
</header>
❌ Incorrect: deeply nested <footer> still inside <header>
<header>
<h1>My Website</h1>
<divclass="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>
<ahref="/">Home</a>
<ahref="/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>
<ahref="/">Home</a>
<ahref="/about">About</a>
</nav>
<divclass="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 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
thcreates 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-tableth{
font-size:1.5rem;
font-weight: bold;
text-transform: uppercase;
}
</style>
<tableclass="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 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
dlelement. - Place the heading inside a
ddelement instead. Theddelement accepts flow content, so headings are valid there. - Style the
dtwith CSS. If you only need the term to look like a heading, apply font size, weight, and other styles directly to thedtwithout 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>
<dtclass="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><spanclass="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 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
thtext if you need a particular visual appearance. - Use the
captionelement 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-headerth{
font-size:1.5em;
font-weight: bold;
color:#333;
}
</style>
<tableclass="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.
Heading elements like h3 cannot be placed inside a dt element because dt only accepts phrasing content, and headings are flow content.
The <dt> element represents a term in a description list (<dl>). According to the HTML specification, <dt> can only contain phrasing content — things like text, <span>, <strong>, <em>, and similar inline-level elements. Heading elements (<h1> through <h6>) are flow content, not phrasing content, so nesting them inside <dt> is invalid.
If you need the text inside <dt> to look like a heading, use CSS to style it instead. Alternatively, if the heading is meant to introduce a group of terms, place it before the <dl> or use the <dfn> element inside the <dt> for emphasis on the term being defined.
Invalid Example
<dl>
<dt><h3>Term Title</h3></dt>
<dd>Description of the term.</dd>
</dl>
Valid Example
Style the <dt> directly with CSS to achieve the visual appearance you want:
<dl>
<dtclass="term-title">Term Title</dt>
<dd>Description of the term.</dd>
</dl>
<style>
.term-title{
font-size:1.17em;
font-weight: bold;
}
</style>
If the heading is meant to introduce the entire list, place it outside:
<h3>Section Title</h3>
<dl>
<dt>Term</dt>
<dd>Description of the term.</dd>
</dl>
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
thelement 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>
<thclass="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. Nesting a heading element like h4 inside it creates a conflict in the document's outline and semantic structure. Screen readers and other assistive technologies treat headings and table headers as distinct navigational landmarks, so combining them can confuse users who rely on these tools to understand page structure. A heading buried inside a table cell may break the expected heading hierarchy, making it harder for users to navigate by headings.
According to the HTML specification, the content model of 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.
The reason developers often place headings inside th is to achieve a specific visual style — larger, bolder text. But th elements are already rendered bold by default in most browsers, and any additional styling should be handled with CSS rather than repurposing heading elements.
How to Fix It
- Remove the heading element from inside the
thand use the text directly. - Style with CSS if you need the
thcontent to look different from default styling. - Move the heading outside the table if it serves as a title or caption for the table. Consider using the
<caption>element for table titles.
Examples
❌ Incorrect: Heading inside th
<table>
<tr>
<th><h4>Product</h4></th>
<th><h4>Price</h4></th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
This triggers the validation error because h4 elements are not allowed as descendants of th.
✅ Fixed: Plain text in th
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
The simplest fix — just remove the heading tags. The th element already conveys that these cells are headers.
✅ Fixed: Using CSS for custom styling
If you need the header cells to have a specific visual appearance, use CSS:
<table>
<tr>
<thclass="styled-header">Product</th>
<thclass="styled-header">Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
<style>
.styled-header{
font-size:1.2em;
text-transform: uppercase;
}
</style>
✅ Fixed: Moving the heading outside and using caption
If the heading was meant to serve as a title for the table, use the <caption> element instead:
<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 purpose-built for labeling tables and is well-supported by assistive technologies. You can also place a heading before the table if it fits your document's heading hierarchy:
<h4>Product Pricing</h4>
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
Both approaches keep your HTML valid while preserving clear semantics for both visual users and assistive technology.
An h5 element cannot be placed inside an element that has role="button" because heading elements are not allowed as descendants of buttons.
Screen readers and other assistive technologies treat buttons as flat, interactive controls. When a heading appears inside a button, the heading semantics are either lost or conflict with the button role. The user expects a button to contain a short label, not a document structure element. The HTML spec and ARIA authoring practices both restrict the content model of elements with role="button" to phrasing content only, which excludes headings (h1 through h6).
If the text inside the button needs to look like a heading visually, apply CSS styling to a span or directly to the button itself. The visual appearance and the semantic meaning are separate concerns.
HTML examples
Invalid: heading inside a button role
<divrole="button"tabindex="0">
<h5>Subscribe now</h5>
</div>
Valid: styled span instead of a heading
<divrole="button"tabindex="0">
<spanclass="button-label">Subscribe now</span>
</div>
.button-label{
font-size:0.83em;
font-weight: bold;
}
If the element is actually meant to function as a heading and not as a button, remove the role="button" and use a proper button or a element nearby instead.
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
thelement. - 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>
<thclass="table-heading">Product</th>
<thclass="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 <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>
<thclass="table-header">Product</th>
<thclass="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 <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>
<ahref="/email">Email</a>
<ahref="/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>
<divclass="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>
<ahref="/email">Email</a>
<ahref="/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>
<ahref="/email">Email</a>
<ahref="/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>
<divclass="footer-top">
<h3>Quick Links</h3>
<nav>
<ahref="/about">About</a>
<ahref="/contact">Contact</a>
</nav>
</div>
<divclass="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 <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><ahref="/toys">Toys</a></li>
<li><ahref="/books">Books</a></li>
<li><ahref="/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 <iframe> element embeds an entirely separate HTML document within the current page, creating its own independent browsing context. The <a> element, on the other hand, is an interactive element designed to navigate users to a new URL or location. When you nest an <iframe> inside an <a>, browsers face a conflict: user interactions like clicks could be intended for the embedded content inside the iframe or for the link itself. The HTML specification resolves this ambiguity by simply disallowing it.
According to the WHATWG HTML living standard, the <a> element's content model does not permit interactive content as descendants. The <iframe> element is categorized as interactive content, which means it must not appear anywhere inside an <a> tag — not as a direct child and not nested deeper within other elements inside the link.
This restriction matters for several reasons:
- Accessibility: Screen readers and assistive technologies cannot reliably convey the purpose of a link that contains an embedded document. Users may not understand whether they are interacting with the link or the iframe.
- Unpredictable behavior: Different browsers may handle clicks on the iframe-inside-a-link differently, leading to inconsistent user experiences.
- Standards compliance: Violating the content model makes your HTML invalid, which can cause unexpected rendering and behavior.
To fix the issue, restructure your markup so the <iframe> and <a> are siblings or otherwise separated. If your goal is to provide a link alongside embedded content, place the link before or after the iframe. If you want a clickable preview that links somewhere, consider using an image thumbnail inside the link instead of an iframe.
Examples
❌ Invalid: <iframe> inside an <a> element
<ahref="https://example.com">
<iframesrc="https://example.com/embed"></iframe>
</a>
This triggers the validation error because the <iframe> is a descendant of the <a> element.
❌ Invalid: <iframe> nested deeper inside an <a> element
<ahref="https://example.com">
<div>
<iframesrc="https://example.com/embed"></iframe>
</div>
</a>
Even though the <iframe> is not a direct child, it is still a descendant of the <a> element, which is not allowed.
✅ Valid: Separate the <iframe> and <a> elements
<ahref="https://example.com">Visit Example.com</a>
<iframesrc="https://example.com/embed"></iframe>
The link and the iframe are siblings, so there is no nesting conflict.
✅ Valid: Use an image as a clickable preview instead
If the intent is to create a clickable preview that links to a page, use a thumbnail image rather than an iframe:
<ahref="https://example.com">
<imgsrc="preview-thumbnail.jpg"alt="Preview of Example.com">
</a>
✅ Valid: Wrap in a container with a separate link
If you need both an iframe and a related link displayed together, use a wrapper element:
<divclass="embed-container">
<iframesrc="https://example.com/embed"title="Embedded content from Example.com"></iframe>
<p><ahref="https://example.com">Open Example.com in a new page</a></p>
</div>
Note that when using <iframe>, it's good practice to include a title attribute to describe the embedded content for accessibility purposes.
The ARIA button role tells browsers and assistive technologies that an element behaves like a button. According to the WAI-ARIA specification, elements with role="button" follow the same content restrictions as native <button> elements. Specifically, they must not contain interactive content as descendants. The <input> element is considered interactive content, so nesting it inside any element with role="button" is invalid.
This restriction exists for important accessibility and usability reasons. When a screen reader encounters an element with role="button", it announces it as a single actionable control. If that button contains another interactive element like an <input>, the user faces conflicting interactions — should activating the element trigger the button action or interact with the input? This ambiguity confuses both assistive technologies and users. Browsers may also handle focus and click events unpredictably when interactive elements are nested this way.
The same rule applies to native <button> elements, <a> elements with an href, and any other element that is already interactive. Adding role="button" to a <div> or <span> elevates it to the same status, so the same nesting restrictions apply.
To fix this issue, consider these approaches:
- Move the
<input>outside the button-role element and position them as siblings. - Replace the
<input>with non-interactive content such as a<span>styled to look like the desired control, with appropriate ARIA attributes to convey state. - Rethink the component structure — if you need both a button action and an input, they should be separate controls that are visually grouped but not nested.
Examples
❌ Invalid: <input> nested inside an element with role="button"
<divrole="button"tabindex="0">
<inputtype="checkbox"/>
Accept terms
</div>
❌ Invalid: <input> nested inside a native <button>
<button>
<inputtype="text"/>
Search
</button>
✅ Valid: Separate the <input> and button into sibling elements
<label>
<inputtype="checkbox"/>
Accept terms
</label>
<button>Submit</button>
✅ Valid: Use non-interactive content inside the button-role element
If you want a toggle-style button that conveys checked/unchecked state, use ARIA attributes on the button itself instead of embedding an <input>:
<divrole="button"tabindex="0"aria-pressed="false">
<spanaria-hidden="true">☐</span>
Accept terms
</div>
✅ Valid: Use a <label> and <input> alongside a button
<div>
<label>
<inputtype="checkbox"/>
Accept terms
</label>
<divrole="button"tabindex="0">Continue</div>
</div>
✅ Valid: Button with only non-interactive phrasing content
<divrole="button"tabindex="0">
<span>Click me</span>
</div>
When in doubt, keep interactive elements as separate, distinct controls rather than nesting them. This ensures clear semantics, predictable behavior across browsers, and a good experience for users of assistive technologies.
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
<ahref="/search">
<inputtype="text"placeholder="Search...">
</a>
This triggers the validation error because <input> is interactive content nested inside <a>.
✅ Correct: Separate the elements
<formaction="/search">
<inputtype="text"placeholder="Search...">
<buttontype="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
<ahref="/settings">
<inputtype="checkbox"id="notify"> Enable notifications
</a>
✅ Correct: Place the link and input as siblings
<label>
<inputtype="checkbox"id="notify"> Enable notifications
</label>
<ahref="/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:
<ahref="/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:
<divclass="search-bar">
<inputtype="text"placeholder="Search...">
<ahref="/search">Go</a>
</div>
❌ Incorrect: Hidden input inside a link
Even hidden or non-visible inputs trigger this error:
<ahref="/page">
<inputtype="hidden"name="ref"value="home">
Click here
</a>
✅ Correct: Move the hidden input outside the link
<inputtype="hidden"name="ref"value="home">
<ahref="/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:
<ahref="/page?ref=home">Click here</a>
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 anhrefattribute)<button><select><textarea><label><audio>and<video>(withcontrols)<embed>,<iframe>,<object>- Any element with a
tabindexattribute
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
<inputtype="text"name="example">
</button>
This triggers the error because <input> is interactive content placed inside a <button>.
✅ Fixed: <input> and <button> as siblings
<form>
<inputtype="text"name="example">
<buttontype="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>:
<buttontype="submit">
Save
<inputtype="hidden"name="action"value="save">
</button>
✅ Fixed: Hidden <input> moved outside <button>
<form>
<inputtype="hidden"name="action"value="save">
<buttontype="submit">Save</button>
</form>
❌ Invalid: Checkbox inside a <button> for a toggle effect
<buttonclass="toggle">
<inputtype="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:
<labelclass="toggle">
<inputtype="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:
<buttontype="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 <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>'sforattribute 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>
<ahref="/settings">
<label>Account Settings</label>
</a>
This triggers the validation error because <label> is interactive content nested inside <a>.
✅ Fixed: Replace <label> with <span>
<ahref="/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>
<ahref="/profile">
<div>
<labelfor="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
<labelfor="username">Edit Username</label>
<ahref="/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
<ahref="/dashboard">
<spanclass="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 <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>
<buttontype="submit">
<label>Submit Form</label>
</button>
❌ Invalid: <label> with a for attribute inside a <button>
<buttontype="button">
<labelfor="file-input">Choose File</label>
</button>
<inputtype="file"id="file-input">
✅ Fixed: Use plain text inside the <button>
<buttontype="submit">
Submit Form
</button>
✅ Fixed: Use a <span> for styling purposes
<buttontype="submit">
<spanclass="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:
<labelfor="username">Username</label>
<inputtype="text"id="username">
<buttontype="submit"aria-label="Submit the username form">
Submit
</button>
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
<inputtype="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>
<labelfor="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
<labelfor="first-name">First Name</label>
<inputtype="text"id="first-name"name="first-name">
<labelfor="last-name">Last Name</label>
<inputtype="text"id="last-name"name="last-name">
✅ Using implicit label association (one label per input)
<label>
First Name
<inputtype="text"name="first-name">
</label>
<label>
Last Name
<inputtype="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>
<labelfor="first-name">First Name</label>
<inputtype="text"id="first-name"name="first-name">
<labelfor="last-name">Last Name</label>
<inputtype="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 <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 <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
<ahref="/comments">
<textareaname="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>
<textareaname="comment"rows="4"cols="40"></textarea>
<ahref="/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
<formaction="/submit-comment"method="post">
<labelfor="comment">Your comment:</label>
<textareaid="comment"name="comment"rows="4"cols="40"></textarea>
<buttontype="submit">Submit</button>
</form>
<ahref="/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 charset meta tag specifies an encoding name that is not the preferred form. Use utf-8 (with a hyphen) instead of utf8.
The HTML specification requires that character encoding declarations use the preferred IANA encoding name. For the Unicode UTF-8 encoding, the preferred name is utf-8, not utf8, UTF8, or other variations. While browsers may still recognize non-preferred names, the W3C validator flags them because the WHATWG HTML standard and IANA character set registry both list utf-8 as the canonical form.
This applies to the <meta charset> declaration and, less commonly, to charset parameters in Content-Type headers or <meta http-equiv> tags.
Incorrect example
<metacharset="utf8">
Correct example
<metacharset="utf-8">
When a <select> element is marked as required, the browser needs a way to determine whether the user has made a deliberate choice. The HTML specification requires that the first <option> element act as a placeholder — a non-selectable default that represents "no choice made." For the browser's constraint validation to work correctly, this placeholder option must have an empty value attribute (value=""), or it must have no text content at all.
This requirement only applies when all three conditions are met:
- The
<select>has arequiredattribute. - The
<select>does not have amultipleattribute. - The
<select>does not have asizeattribute with a value greater than1.
In this configuration, the <select> renders as a standard single-selection dropdown, and the first <option> with an empty value serves as the "please choose" prompt. If the user submits the form without changing the selection from this placeholder, the browser will block submission and display a validation message — just as it would for an empty required text input.
Why this matters
- Form validation: Without a proper placeholder option, the browser may consider the first option as a valid selection, allowing the form to submit even when the user hasn't actively chosen anything. This defeats the purpose of
required. - Accessibility: Screen readers and assistive technologies rely on standard patterns. A placeholder option clearly communicates to all users that a selection is expected.
- Standards compliance: The WHATWG HTML specification explicitly defines this constraint, and violating it produces a validation error.
How to fix it
- Add a placeholder
<option>as the first child of the<select>, withvalue=""and descriptive prompt text (e.g., "Choose an option"). - Alternatively, if you don't want a visible placeholder, the first
<option>can have no text content at all (<option value=""></option>), though this is less user-friendly. - Another approach is to add a
sizeattribute equal to the number of options, or add themultipleattribute — but these change the visual presentation from a dropdown to a list box, so they're only appropriate if that's the desired UI.
Examples
❌ Incorrect: first option has a non-empty value
<selectrequired>
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
Here, "Small" is preselected and has a non-empty value. The browser treats it as a valid choice, so required validation never triggers — the form can be submitted without the user making an active decision.
❌ Incorrect: placeholder option has a non-empty value
<selectrequired>
<optionvalue="none">Choose a size</option>
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
The first option looks like a placeholder, but its value is "none" rather than empty. The validator flags this because the browser considers "none" a valid value.
✅ Correct: placeholder option with an empty value
<selectrequired>
<optionvalue="">Choose a size</option>
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
The first <option> has value="" and serves as a clear prompt. If the user doesn't select a different option, form validation will prevent submission.
✅ Correct: placeholder option with no text content
<selectrequired>
<optionvalue=""></option>
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
This also satisfies the constraint, though it may appear as a blank entry in the dropdown. It can work in cases where a <label> already makes the expected action clear.
✅ Correct: using a size attribute to avoid the requirement
<selectrequiredsize="3">
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
By adding size="3" (equal to the number of options), the <select> renders as a list box rather than a dropdown. The placeholder requirement no longer applies because no option is implicitly preselected — the user must click to choose. Note that this changes the visual appearance significantly.
The HTML specification requires that every id attribute value must be unique within a document. When the validator encounters the same id on two or more elements, it raises an error on the second (and subsequent) occurrences, along with a companion message — "The first occurrence of ID 'X' was here" — pointing to the original element. This companion message helps you quickly compare both locations and decide which one to rename or remove.
Why Duplicate IDs Are a Problem
Accessibility: Screen readers and other assistive technologies rely on unique IDs to associate <label> elements with form controls, to navigate ARIA relationships like aria-labelledby and aria-describedby, and to jump to page landmarks. When IDs are duplicated, these associations break, leaving users confused or unable to interact with the page properly.
JavaScript behavior: Methods like document.getElementById() return only the first matching element. If you intend to target the second element with a duplicated ID, your code will silently operate on the wrong one. This can lead to bugs that are difficult to track down.
CSS specificity: While #my-id selectors will style all elements with that ID in most browsers, this is non-standard behavior. Relying on it leads to fragile, unpredictable styling.
Fragment navigation: Links using href="#section" scroll to the first element with that ID. Duplicate IDs make it impossible to link to the second occurrence.
How to Fix It
- Identify the duplicates. The validator tells you the line number of the first occurrence and the line number of the duplicate. Compare both elements.
- Rename one of the IDs to something unique and descriptive. Update any corresponding references (labels, ARIA attributes, JavaScript selectors, CSS rules, and anchor links).
- Use
classinstead ofidwhen you need to apply the same style or behavior to multiple elements. Classes are designed to be reused; IDs are not.
Examples
❌ Duplicate IDs trigger the error
<divid="product-card">
<h2>Widget A</h2>
<p>A useful widget.</p>
</div>
<divid="product-card">
<h2>Widget B</h2>
<p>Another useful widget.</p>
</div>
The validator will report an error on the second div and display the message "The first occurrence of ID 'product-card' was here" pointing to the first div.
✅ Fixed: Give each element a unique ID
<divid="product-card-a">
<h2>Widget A</h2>
<p>A useful widget.</p>
</div>
<divid="product-card-b">
<h2>Widget B</h2>
<p>Another useful widget.</p>
</div>
✅ Fixed: Use a class for shared styling or behavior
If both elements don't need to be individually targeted, replace the id with a class:
<divclass="product-card">
<h2>Widget A</h2>
<p>A useful widget.</p>
</div>
<divclass="product-card">
<h2>Widget B</h2>
<p>Another useful widget.</p>
</div>
❌ Duplicate IDs breaking a label association
<labelfor="email">Email</label>
<inputtype="email"id="email"name="primary-email">
<labelfor="email">Backup Email</label>
<inputtype="email"id="email"name="backup-email">
Both labels point to for="email", but only the first input will be associated. The second label effectively has a broken link.
✅ Fixed: Unique IDs for each form control
<labelfor="primary-email">Email</label>
<inputtype="email"id="primary-email"name="primary-email">
<labelfor="backup-email">Backup Email</label>
<inputtype="email"id="backup-email"name="backup-email">
Now each <label> correctly associates with its own <input>, and both assistive technologies and JavaScript can target each field reliably.
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