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 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 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.
An <a> element with an href attribute is interactive content, and interactive content cannot appear inside another <a> element.
The href attribute is what triggers this error. A link that points somewhere is interactive, and the HTML specification does not allow one interactive element to contain another. The outer <a> is already interactive, so a second linked <a> nested anywhere inside it, even several elements deep, makes the markup invalid. An <a> without href is just a placeholder and is allowed inside a link, which is why the validator names the attribute explicitly.
When links overlap like this, browsers cannot tell which destination a click should follow, and most will silently close the outer link before the inner one, producing a DOM that no longer matches your source. Screen readers run into the same ambiguity and may announce the wrong link or skip one entirely.
The fix is to give each link its own space so that no <a href> sits inside another <a href>.
Invalid example
<a href="/products">
Browse our <a href="/products/new">new arrivals</a> today
</a>
Valid example
<p>
<a href="/products">Browse our products</a> or check out our
<a href="/products/new">new arrivals</a> today.
</p>
An <a> element cannot be placed inside a <button> element because interactive content must not be nested within other interactive content.
The HTML specification forbids nesting clickable elements inside other clickable elements. Both <a> and <button> are interactive content, meaning they each expect to receive user interaction independently. When you nest one inside the other, browsers can't determine which element should handle the click, leading to unpredictable behavior and accessibility problems.
Screen readers and keyboard navigation also struggle with nested interactive elements. A user tabbing through the page may not be able to reach or activate the inner link, or may trigger the wrong action entirely.
To fix this, you have two main options: use a styled <a> element that looks like a button, or use a <button> with JavaScript to handle navigation.
Invalid Example
<button>
<a href="/dashboard">Go to Dashboard</a>
</button>
Fixed Examples
Option 1: Style the link as a button
This is the preferred approach when the purpose is navigation.
<a href="/dashboard" class="btn">Go to Dashboard</a>
<style>
.btn {
display: inline-block;
padding: 8px 16px;
background-color: #007bff;
color: #fff;
text-decoration: none;
border-radius: 4px;
border: none;
cursor: pointer;
}
</style>
Option 2: Use a button with JavaScript
This works when you need button semantics but also want navigation.
<button type="button" onclick="location.href='/dashboard'">
Go to Dashboard
</button>
Option 1 is generally better for navigation because <a> elements communicate the correct intent to assistive technologies and allow standard browser behaviors like right-click "Open in new tab."
The role="button" attribute tells assistive technologies like screen readers that an element behaves as a button — a widget used to perform actions such as submitting a form, opening a dialog, or triggering a command. When a <button> element appears inside an element with role="button", the result is a nested interactive control. The HTML specification explicitly forbids this because interactive content must not be nested within other interactive content.
This nesting causes real problems. Screen readers may announce the outer element as a button but fail to recognize or reach the inner <button>. Keyboard users may not be able to focus on or activate the inner control. Different browsers handle the situation inconsistently — some may ignore one of the controls entirely, others may fire events on the wrong element. The end result is an interface that is broken for many users.
This issue commonly arises in a few scenarios:
- A
<div>or<span>is givenrole="button"and then a<button>is placed inside it for styling or click-handling purposes. - A component library wraps content in a
role="button"container, and a developer adds a<button>inside without realizing the conflict. - A custom card or list item is made clickable with
role="button", but also contains action buttons within it.
The fix depends on your intent. If the outer element is the intended interactive control, remove the inner <button> and handle interactions on the outer element. If the inner <button> is the intended control, remove role="button" from the ancestor. If both need to be independently clickable, restructure the markup so neither is a descendant of the other.
Examples
❌ Incorrect: <button> inside an element with role="button"
<div role="button" tabindex="0" onclick="handleClick()">
<button type="button">Click me</button>
</div>
This is invalid because the <button> is a descendant of the <div> that has role="button".
✅ Fix option 1: Use only the <button> element
If the inner <button> is the actual control, remove role="button" from the wrapper:
<div>
<button type="button" onclick="handleClick()">Click me</button>
</div>
✅ Fix option 2: Use only the outer role="button" element
If the outer element is the intended interactive control, remove the inner <button>:
<div role="button" tabindex="0" onclick="handleClick()">
Click me
</div>
Note that when using role="button" on a non-<button> element, you must also handle keyboard events (Enter and Space) manually. A native <button> provides this for free, so prefer option 1 when possible.
❌ Incorrect: Clickable card containing action buttons
<div role="button" tabindex="0" class="card">
<h3>Item title</h3>
<p>Description text</p>
<button type="button">Delete</button>
</div>
✅ Fix: Separate the card link from the action buttons
<div class="card">
<h3><button type="button" class="card-link">Item title</button></h3>
<p>Description text</p>
<button type="button">Delete</button>
</div>
In this approach, the card's main action is handled by a <button> on the title, while the "Delete" button remains an independent control. Neither is nested inside the other, and both are accessible to keyboard and screen reader users.
A <button> element must not be placed inside any element that carries role="img", because that role tells assistive technologies to treat the element and everything inside it as a single, flat image.
When you set role="img" on a container, screen readers stop exposing its children as separate, operable controls. The whole subtree collapses into one graphic with a single accessible name. A <button> nested inside disappears from that flattened view: a screen reader user cannot reach or activate it, even though the button still renders and responds to a mouse. The checker flags this because the markup promises an image but hides an interactive control inside it.
This usually happens when role="img" is added to a wrapper that groups an icon or illustration together with a real control, such as a decorative card that also holds a button.
The fix is to decide what the element actually is. If it is an image, keep role="img" and move the button outside it. If it needs a button, remove role="img" from the ancestor and describe any decorative graphics with alt text or aria-label on the image itself.
Invalid example
<div role="img" aria-label="Play the intro video">
<img src="thumbnail.jpg" alt="">
<button type="button">Play</button>
</div>
Valid example
<button type="button" aria-label="Play the intro video">
<img src="thumbnail.jpg" alt="">
</button>
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 <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 <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.
Heading elements <h1> through <h6> must not appear inside an element with role="button". ARIA exposes a button as a single, flat control and treats its contents as presentational, so a heading placed inside it is no longer announced as a heading. Move the heading outside the control, or use a native <button> for the part that is actually clickable.
The WAI-ARIA specification marks button as a role whose children are presentational. When the browser maps a role="button" element into the accessibility tree, it drops the roles of everything inside it, so the heading level is lost. The W3C validator reports the conflict instead of letting the heading semantics disappear silently.
This usually happens when a clickable card or toggle is built from a <div role="button"> that wraps a heading. Headings are navigation points that screen reader users jump between, and they form the document outline. Burying one inside a button removes it from heading navigation and flattens the outline.
Keep the heading as a sibling of the control rather than a descendant. When the whole region needs to be activatable, use a native <button> (or a link) for the action and leave the heading outside it.
Invalid example
<div role="button">
<h6>Save changes</h6>
</div>
Valid example
<h6>Settings</h6>
<button type="button">Save changes</button>
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-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 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>
<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 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-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.
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>
<dt class="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>
<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. 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>
<th class="styled-header">Product</th>
<th class="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
<div role="button" tabindex="0">
<h5>Subscribe now</h5>
</div>
Valid: styled span instead of a heading
<div role="button" tabindex="0">
<span class="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>
<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 <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 <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 <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 <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
<a href="https://example.com">
<iframe src="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
<a href="https://example.com">
<div>
<iframe src="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
<a href="https://example.com">Visit Example.com</a>
<iframe src="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:
<a href="https://example.com">
<img src="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:
<div class="embed-container">
<iframe src="https://example.com/embed" title="Embedded content from Example.com"></iframe>
<p><a href="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"
<div role="button" tabindex="0">
<input type="checkbox" />
Accept terms
</div>
❌ Invalid: <input> nested inside a native <button>
<button>
<input type="text" />
Search
</button>
✅ Valid: Separate the <input> and button into sibling elements
<label>
<input type="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>:
<div role="button" tabindex="0" aria-pressed="false">
<span aria-hidden="true">☐</span>
Accept terms
</div>
✅ Valid: Use a <label> and <input> alongside a button
<div>
<label>
<input type="checkbox" />
Accept terms
</label>
<div role="button" tabindex="0">Continue</div>
</div>
✅ Valid: Button with only non-interactive phrasing content
<div role="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
<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>
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
<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.
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