Guías HTML para a
Aprende a identificar y corregir errores comunes de validación HTML marcados por el W3C Validator, para que tus páginas cumplan con los estándares y se muestren correctamente en todos los navegadores. También consulta nuestras Guías de accesibilidad.
The type attribute on an <a> element is an advisory hint that tells the browser what media type (MIME type) to expect at the linked resource. A valid MIME type follows a strict format: a type, a / separator, and a subtype (e.g., text/html, application/pdf, image/png). Each part must consist of token characters — letters, digits, and certain symbols — but not spaces.
This validation error occurs when the MIME type value contains a space or other unexpected character in a position where only token characters or a / are allowed. Common causes include:
- Accidental spaces within the MIME type (e.g., application/ pdf or application /pdf).
- Multiple MIME types separated by spaces (e.g., text/html text/plain), which is not valid since the attribute accepts only a single MIME type.
- Typos or copy-paste errors that introduce whitespace or non-token characters.
While the type attribute is purely advisory and browsers won’t refuse to follow a link based on it, an invalid value defeats its purpose and signals sloppy markup. Standards-compliant HTML ensures your pages are interpreted consistently and avoids confusing tools, screen readers, or other user agents that may parse this attribute.
Examples
Incorrect: Space within the MIME type
<a href="report.pdf" type="application/ pdf">Download Report</a>
The space after the / makes this an invalid MIME type.
Incorrect: Multiple MIME types separated by a space
<a href="data.csv" type="text/csv text/plain">Download Data</a>
The type attribute only accepts a single MIME type. The space between text/csv and text/plain triggers the error.
Incorrect: Leading or trailing spaces
<a href="photo.jpg" type=" image/jpeg ">View Photo</a>
Spaces before or after the MIME type are not permitted.
Correct: Valid MIME type with no spaces
<a href="report.pdf" type="application/pdf">Download Report</a>
Correct: Other common valid MIME types
<a href="data.csv" type="text/csv">Download Data</a>
<a href="photo.jpg" type="image/jpeg">View Photo</a>
<a href="archive.zip" type="application/zip">Download Archive</a>
Correct: MIME type with a parameter
MIME types can include parameters separated by a semicolon — no spaces are required, though a single space after the semicolon is permitted per the MIME specification:
<a href="page.html" type="text/html; charset=utf-8">View Page</a>
How to Fix
- Inspect the type value — look for any spaces within the type or subtype portions (before or after the /).
- Remove extra spaces — ensure the value is a single, properly formatted MIME type like type/subtype.
- Use only one MIME type — if you’ve listed multiple types, pick the one that accurately describes the linked resource.
- Verify the MIME type is valid — consult the IANA Media Types registry to confirm you’re using a recognized type.
- Consider removing the attribute — since type is purely advisory on <a> elements, if you’re unsure of the correct MIME type, omitting the attribute entirely is perfectly valid.
A MIME type (also called a media type) always follows the format type/subtype, such as text/html, application/pdf, or image/jpeg. The “type” part indicates the general category (e.g., text, image, application, audio, video), and the “subtype” specifies the exact format within that category. When the validator reports “Subtype missing,” it means the value you provided either lacks the /subtype portion or isn’t a valid MIME type structure at all.
A common cause of this error is misunderstanding the purpose of the type attribute on <a> elements. The type attribute is not used to change the behavior or appearance of the link (the way type works on <input> or <button> elements). Instead, it serves as an advisory hint to the browser about what kind of resource the link points to. The browser may use this information to adjust its UI — for example, showing a download prompt for application/pdf — but it is not required to act on it.
Because of this misunderstanding, developers sometimes write type="button" on an <a> element, thinking it will make the link behave like a button. The value button is not a valid MIME type (it has no subtype), so the validator flags it. If you need a button, use a <button> element instead. If you need a styled link that looks like a button, keep the <a> element and use CSS for styling.
Why this matters
- Standards compliance: The HTML specification requires the type attribute on <a> to be a valid MIME type string. An invalid value violates the spec and may be ignored by browsers or cause unexpected behavior.
- Accessibility and semantics: Using type="button" on a link can create confusion about the element’s role. Screen readers and assistive technologies rely on correct semantics to convey meaning to users.
- Browser behavior: While browsers are generally forgiving, an invalid type value provides no useful information and could interfere with how the browser handles the linked resource.
How to fix it
- If you intended to hint at the linked resource’s MIME type, make sure you provide a complete type/subtype value — for example, application/pdf rather than just application.
- If you used type to try to style or change the link’s behavior, remove the type attribute entirely. Use CSS for visual styling or switch to a more appropriate element like <button>.
- If you don’t need the type attribute, simply remove it. It’s entirely optional on <a> elements.
Examples
Incorrect: missing subtype
<a href="report.pdf" type="application">Download report</a>
The value application is incomplete — it’s missing the subtype portion after the slash.
Incorrect: not a MIME type at all
<a href="/order.php" type="button">Submit</a>
The value button is not a MIME type. This often stems from confusing the type attribute on <a> with the type attribute on <input> or <button>.
Correct: valid MIME type
<a href="report.pdf" type="application/pdf">Download report</a>
<a href="photo.jpeg" type="image/jpeg">See a photo</a>
The type attribute uses a properly formatted MIME type with both a type and subtype.
Correct: removing the attribute entirely
<a href="/order.php">Submit</a>
If the type attribute isn’t serving a real purpose, the simplest fix is to remove it.
Correct: using a button element instead
<button type="submit">Submit</button>
If you need actual button behavior (such as submitting a form), use a <button> element rather than an <a> element with an invalid type.
According to the HTML specification, the <a> element can exist without an href attribute, but in that case it represents a placeholder where a link might otherwise have been placed. However, the validator flags this as an issue because an <a> element without href and without role is ambiguous — browsers won’t treat it as a link (it won’t be focusable or keyboard-accessible), and assistive technologies won’t know how to present it to users.
This matters for several reasons:
- Accessibility: Without href, the <a> element loses its implicit role="link" and is no longer announced as a link by screen readers. It also won’t appear in the tab order, making it invisible to keyboard users.
- Semantics: If the element is styled and scripted to behave like a button or link but lacks the proper attributes, it creates a disconnect between what users see and what the browser understands.
- Standards compliance: The spec expects you to be explicit about the element’s purpose when href is absent.
The most common cause of this issue is using <a> elements as JavaScript-only click targets without providing an href, or using them as styled containers without any interactive purpose.
How to Fix It
There are several approaches depending on your intent:
- Add an href attribute if the element should be a link. This is the most common and recommended fix.
- Add a role attribute if you’re deliberately using <a> without href for a specific purpose, such as role="button".
- Use a different element entirely. If it’s not a link, consider using a <button>, <span>, or another semantically appropriate element.
Examples
❌ Missing both href and role
<a>Click here</a>
This triggers the validator error because the <a> has neither href nor role.
❌ JavaScript-only handler without href
<a onclick="doSomething()">Submit</a>
Even with an event handler, the element lacks href and role, so it fails validation and is inaccessible to keyboard users.
✅ Fix by adding an href
<a href="/about">About us</a>
Adding href makes it a proper hyperlink — focusable, keyboard-accessible, and recognized by screen readers.
✅ Fix by adding role for a non-link purpose
<a role="button" tabindex="0" onclick="doSomething()">Submit</a>
If you must use <a> without href as a button, add role="button" and tabindex="0" to ensure it’s focusable and properly announced. However, consider using a real <button> instead.
✅ Better: use the right element
<button type="button" onclick="doSomething()">Submit</button>
If the element triggers an action rather than navigating somewhere, a <button> is the correct semantic choice. It’s focusable by default, responds to keyboard events, and doesn’t need extra attributes.
✅ Placeholder anchor (intentional non-link)
<a role="link" aria-disabled="true">Coming soon</a>
If you’re intentionally showing a placeholder where a link will eventually appear, you can add a role and indicate its disabled state for assistive technologies. Alternatively, use a <span> with appropriate styling to avoid the issue altogether.
The HTML specification explicitly forbids nesting <a> elements inside other <a> elements. This is defined as part of the content model for the <a> element — it is “transparent” but must not contain any interactive content, which includes other <a> elements. When the validator encounters a closing </a> tag that would violate these nesting rules, it raises this error.
This typically happens in one of two scenarios:
- A missing closing </a> tag — You forget to close one link, so the next link appears to be nested inside it.
- Intentionally wrapping one link inside another — You try to place a clickable link inside a larger clickable area, which is invalid HTML.
Why this matters
When an <a> element is nested inside another <a> element, browsers must guess what you intended. Different browsers may handle this differently — some will auto-close the first link before starting the second, while others may produce unexpected DOM structures. This leads to:
- Unpredictable behavior — Click targets may not work as expected across different browsers.
- Accessibility issues — Screen readers rely on a well-structured DOM. Ambiguous nesting confuses assistive technologies and makes navigation difficult for users who depend on them.
- Broken styling — CSS selectors that depend on proper parent-child relationships may not apply correctly.
How to fix it
- Find the offending <a> tags — The validator will point to the line with the problematic closing </a>. Look at that line and the lines above it to find where the nesting issue begins.
- Add missing closing tags — If you forgot a </a>, add it before the next <a> opens.
- Restructure if needed — If you intended to have a link inside a larger clickable area, redesign the markup so that the links are siblings rather than nested.
Examples
❌ Missing closing tag causes implicit nesting
The first <a> is never closed, so the second <a> appears to be nested inside it:
<nav>
<a href="one.html">Page 1
<a href="two.html">Page 2</a>
</nav>
✅ Fixed by adding the missing closing tag
<nav>
<a href="one.html">Page 1</a>
<a href="two.html">Page 2</a>
</nav>
❌ Intentionally nesting links (invalid)
Wrapping a link inside a larger link is not allowed, even if it seems useful for UI purposes:
<a href="/article">
<h2>Article Title</h2>
<p>A short summary of the article.</p>
<a href="/author">Author Name</a>
</a>
✅ Fixed by restructuring with sibling links
Use CSS positioning or a different layout strategy to achieve the same visual result without nesting:
<article>
<a href="/article">
<h2>Article Title</h2>
<p>A short summary of the article.</p>
</a>
<p>By <a href="/author">Author Name</a></p>
</article>
❌ Forgetting to close links in a list
This is especially common in navigation menus built with lists:
<ul>
<li><a href="/home">Home</li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
Here, the first <a> is never closed. The </li> tag implicitly closes the <li>, but the <a> remains open, causing nesting issues with the subsequent links.
✅ Fixed by properly closing each link
<ul>
<li><a href="/home">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
A good habit is to write both the opening and closing <a> tags together before filling in the content. This prevents accidental omission and keeps your HTML well-structured.
The HTML specification defines <a> elements as having a transparent content model, but with specific exceptions — most notably, they must not contain interactive content, which includes other <a> elements. This means you can never legally nest one link inside another, regardless of how deeply nested or how the markup is structured.
This error most commonly occurs for one of two reasons:
- A missing closing tag: You forgot to close a previous <a> element with </a>, so the browser (and the validator) considers the next <a> tag to be nested inside the still-open one.
- Intentionally nested links: You tried to place a link inside another link, perhaps to create a card component where both the card and an inner element are clickable.
Why this matters
- Unpredictable browser behavior: When browsers encounter nested <a> elements, they attempt to fix the invalid markup using error-recovery algorithms, but different browsers may handle it differently. This can lead to broken links, missing content, or unexpected DOM structures.
- Accessibility issues: Screen readers and other assistive technologies rely on a well-formed DOM. Nested links create confusing navigation — a user tabbing through links may encounter unexpected behavior or miss content entirely.
- Standards compliance: The WHATWG HTML specification explicitly states that <a> elements must not have interactive content descendants, including other <a> elements.
Examples
Missing closing tag (most common cause)
The most frequent trigger is simply forgetting to close an <a> tag. The validator sees the second <a> as being inside the first:
<!-- ❌ Bad: first <a> is never closed -->
<nav>
<a href="one.html">Page 1
<a href="two.html">Page 2</a>
</nav>
Add the missing </a> to fix it:
<!-- ✅ Good: both <a> elements are properly closed -->
<nav>
<a href="one.html">Page 1</a>
<a href="two.html">Page 2</a>
</nav>
Intentionally nested links
Sometimes developers try to nest links when building clickable card components:
<!-- ❌ Bad: <a> nested inside another <a> -->
<a href="/article" class="card">
<h2>Article Title</h2>
<p>A short description of the article.</p>
<a href="/author">Author Name</a>
</a>
There are several valid approaches to fix this. One common pattern is to make the card a non-link container and use CSS to stretch the primary link over the entire card:
<!-- ✅ Good: no nested links, card is still fully clickable -->
<div class="card">
<h2><a href="/article" class="card-link">Article Title</a></h2>
<p>A short description of the article.</p>
<a href="/author">Author Name</a>
</div>
.card {
position: relative;
}
.card-link::after {
content: "";
position: absolute;
inset: 0;
}
.card a:not(.card-link) {
position: relative;
z-index: 1;
}
This technique uses a pseudo-element to expand the primary link’s clickable area over the entire card, while the secondary link (Author Name) sits above it via z-index, remaining independently clickable.
Links wrapping block-level content
In HTML5, <a> elements can wrap block-level elements like <div> and <h2>, which is perfectly valid. Just make sure you don’t accidentally nest another <a> inside:
<!-- ✅ Good: <a> wrapping block content with no nested links -->
<a href="/article">
<div class="card">
<h2>Article Title</h2>
<p>A short description of the article.</p>
</div>
</a>
Quick checklist
- Search your HTML for every <a tag and verify it has a corresponding </a>.
- If you’re generating HTML dynamically (with a CMS, templating engine, or JavaScript), check that your template logic doesn’t produce unclosed or nested anchors.
- If you need multiple clickable areas within a single component, use the CSS pseudo-element overlay technique shown above instead of nesting links.
The HTML <table> element follows a rigid structure. A <table> can only contain <caption>, <colgroup>, <thead>, <tbody>, <tfoot>, and <tr> as direct children. A <tr> can only contain <td> and <th> elements. Placing an <a> tag (or any other inline/flow content) directly inside <table>, <tr>, or other table-structural elements violates this content model, triggering the “Start tag a seen in table“ validation error.
This matters for several reasons. Browsers handle invalid table markup inconsistently — some may silently move the misplaced <a> element outside the table entirely, while others may render it in unexpected positions. This leads to unpredictable layouts and broken links. Screen readers and other assistive technologies rely on proper table structure to navigate content, so misplaced elements can make the page confusing or inaccessible. Additionally, search engine crawlers may not correctly associate the link with its intended context.
The fix is straightforward: always wrap inline content like <a> inside a <td> or <th> element. Every piece of visible content in a table must live inside a table cell.
Examples
❌ Incorrect: <a> directly inside <tr>
<table>
<tr>
<a href="/details">View details</a>
</tr>
</table>
The <a> is a direct child of <tr>, which only allows <td> and <th> children.
❌ Incorrect: <a> directly inside <table>
<table>
<a href="/details">View details</a>
<tr>
<td>Data</td>
</tr>
</table>
The <a> is a direct child of <table>, which does not allow inline content.
❌ Incorrect: <a> directly inside <tbody>
<table>
<tbody>
<a href="/details">View details</a>
<tr>
<td>Data</td>
</tr>
</tbody>
</table>
The <a> is placed directly inside <tbody>, which only allows <tr> and <script>/<template> elements.
✅ Correct: <a> inside a <td>
<table>
<tr>
<td>
<a href="/details">View details</a>
</td>
</tr>
</table>
✅ Correct: <a> inside a <th>
<table>
<thead>
<tr>
<th>
<a href="/details">View details</a>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Data</td>
</tr>
</tbody>
</table>
✅ Correct: wrapping an entire row’s content in a link
If you want to make an entire row clickable, place the <a> inside each <td> rather than wrapping the <tr>:
<table>
<tr>
<td><a href="/item/1">Product name</a></td>
<td><a href="/item/1">$19.99</a></td>
<td><a href="/item/1">In stock</a></td>
</tr>
</table>
This keeps the table structure valid while still providing clickable content across the row. You can use CSS to remove the visual link styling and make each <a> fill its entire cell for a seamless clickable-row effect.
An aria-label attribute on an <a> element is only valid when the link has an accessible role that supports naming — which means the <a> must have an href attribute or an explicit role that accepts a label.
When an <a> element lacks an href attribute, it has the implicit role of generic. The generic role is in the list of roles that do not support naming, so applying aria-label to it is invalid. This is because a generic element has no semantic meaning, and screen readers wouldn’t know how to announce the label in a meaningful way.
The most common cause of this error is using <a> as a placeholder or JavaScript-only trigger without an href. An <a> with an href has the implicit role of link, which does support aria-label, so the error won’t appear.
You have a few ways to fix this:
- Add an href to make it a proper link (most common fix).
- Add an explicit role that supports naming, such as role="button", if the element acts as a button.
- Use a <button> instead if the element triggers an action rather than navigation.
- Remove aria-label if it’s not needed, and use visible text content instead.
HTML Examples
❌ Invalid: aria-label on an <a> without href
<a aria-label="Close menu" onclick="closeMenu()">✕</a>
The <a> has no href, so its implicit role is generic, which does not support naming.
✅ Fix option 1: Add an href
<a href="/close" aria-label="Close menu">✕</a>
✅ Fix option 2: Use a <button> instead
<button aria-label="Close menu" onclick="closeMenu()">✕</button>
✅ Fix option 3: Add an explicit role that supports naming
<a role="button" tabindex="0" aria-label="Close menu" onclick="closeMenu()">✕</a>
Using a <button> (option 2) is generally the best choice for interactive elements that perform actions rather than navigate to a URL.
The WAI-ARIA specification defines strict rules about which elements can be children of interactive roles. An element with role="button" is treated as an interactive control, and the <a> element (when it has an href) is also an interactive control. Nesting one interactive element inside another creates what’s known as an interactive content nesting violation. Screen readers and other assistive technologies cannot reliably determine user intent when they encounter this pattern — should they announce a button, a link, or both? The result is unpredictable behavior that can make your content inaccessible.
This issue commonly appears when developers wrap links inside styled containers that have been given role="button", or when using component libraries that apply button semantics to wrapper elements containing anchor tags.
Beyond accessibility, browsers themselves handle nested interactive elements inconsistently. Some may ignore the outer interactive role, while others may prevent the inner link from functioning correctly. This makes the pattern unreliable even for users who don’t rely on assistive technologies.
How to fix it
There are several approaches depending on your intent:
- If the element should navigate to a URL, use an <a> element and style it to look like a button. Remove the role="button" from the parent or eliminate the parent wrapper entirely.
- If the element should perform an action (not navigation), use a <button> element or an element with role="button", and handle the action with JavaScript. Remove the nested <a> tag.
- If you need both a button container and a link, flatten the structure so they are siblings rather than nested.
When using role="button" on a non-button element, remember that you must also handle keyboard interaction (Enter and Space key presses) and include tabindex="0" to make it focusable.
Examples
❌ Incorrect: link nested inside a button role
<div role="button">
<a href="/dashboard">Go to Dashboard</a>
</div>
This triggers the validation error because the <a> is a descendant of an element with role="button".
✅ Fix 1: Use a styled link instead
If the intent is navigation, remove the button role and let the <a> element do the job. Style it to look like a button with CSS.
<a href="/dashboard" class="btn">Go to Dashboard</a>
This is the simplest and most semantically correct fix when the purpose is to navigate the user to another page.
✅ Fix 2: Use a real button for actions
If the intent is to trigger an action (not navigate), replace the entire structure with a <button> element.
<button type="button" class="btn" onclick="navigateToDashboard()">
Go to Dashboard
</button>
✅ Fix 3: Use a div with role="button" without nested interactive elements
If you need a custom button using role="button", make sure it contains no interactive descendants.
<div role="button" tabindex="0">
Go to Dashboard
</div>
When using this approach, you must also add keyboard event handlers for Enter and Space to match native button behavior.
❌ Incorrect: link inside a button element
The same principle applies to native <button> elements, not just elements with role="button":
<button>
<a href="/settings">Settings</a>
</button>
✅ Fixed: choose one interactive element
<a href="/settings" class="btn">Settings</a>
❌ Incorrect: deeply nested link
The error applies to any level of nesting, not just direct children:
<div role="button">
<span class="icon-wrapper">
<a href="/help">Help</a>
</span>
</div>
✅ Fixed: flatten the structure
<a href="/help" class="btn">
<span class="icon-wrapper">Help</span>
</a>
As a rule of thumb, every interactive element in your page should have a single, clear role. If something looks like a button but navigates to a URL, make it an <a> styled as a button. If it performs an in-page action, make it a <button>. Keeping these roles distinct ensures your HTML is valid, accessible, and behaves consistently across browsers and assistive technologies.
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.
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 <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 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 <a> element is classified as interactive content, meaning it expects user interaction (clicking to navigate). The <label> element is also interactive — clicking a label activates or focuses its associated form control. When a <label> is nested inside an <a>, the browser faces an ambiguous situation: should a click navigate to the link’s URL, or should it focus/activate the associated form control? The HTML specification resolves this by simply disallowing the nesting entirely.
According to the WHATWG HTML Living Standard, the content model of the <a> element is “transparent” but must not contain any interactive content. Since <label> is interactive content, it is not permitted as a descendant of <a> at any depth.
Beyond being invalid HTML, this nesting causes real problems:
- Accessibility: Screen readers may announce conflicting roles, confusing users who rely on assistive technology. The purpose of the element becomes unclear — is it a link or a form label?
- Unpredictable behavior: Different browsers may handle the click event differently, leading to inconsistent user experiences.
- Broken form association: The <label>‘s for attribute may not work as intended when the label is trapped inside a link.
The fix is straightforward: if you only need to style text inside a link, use a <span> or another non-interactive element instead of <label>. If you genuinely need both a link and a label, they should be separate, sibling elements rather than nested.
Examples
❌ Invalid: <label> inside <a>
<a href="/settings">
<label>Account Settings</label>
</a>
This triggers the validation error because <label> is interactive content nested inside <a>.
✅ Fixed: Replace <label> with <span>
<a href="/settings">
<span>Account Settings</span>
</a>
If the <label> was only used for styling purposes, a <span> with a CSS class achieves the same visual result without violating the specification.
❌ Invalid: <label> deeply nested inside <a>
<a href="/profile">
<div>
<label for="username">Edit Username</label>
</div>
</a>
The rule applies to all descendants, not just direct children. This is still invalid.
✅ Fixed: Separate the link and label
<label for="username">Edit Username</label>
<a href="/profile">View Profile</a>
When you need both a functional label and a link, keep them as siblings rather than nesting one inside the other.
✅ Fixed: Using <span> with a class for styling
<a href="/dashboard">
<span class="label-style">Dashboard</span>
</a>
.label-style {
font-weight: bold;
text-transform: uppercase;
}
This preserves any visual styling you need while keeping the HTML valid and the interaction model unambiguous.
The <a> element is classified as interactive content, and the HTML spec explicitly states that interactive content must not be nested inside other interactive content. A <textarea> is a form control that accepts user input—clicking, focusing, typing, and selecting text within it. When it’s wrapped in a link, the browser faces a conflict: should a click focus the textarea or follow the link? Different browsers may resolve this differently, leading to inconsistent behavior.
Beyond browser inconsistency, this nesting creates serious accessibility problems. Screen readers and other assistive technologies rely on a clear, predictable document structure. When a form control is buried inside a link, the roles and interaction models overlap, making it confusing or even impossible for users relying on keyboard navigation or screen readers to interact with either element properly.
The fix depends on what you’re trying to achieve. If the <textarea> and the link are logically separate, simply move them to be siblings rather than nesting one inside the other. If you need them to appear visually grouped, use a wrapper <div> or another non-interactive container element instead.
Examples
❌ Invalid: <textarea> inside an <a> element
<a href="/comments">
<textarea name="comment" rows="4" cols="40"></textarea>
</a>
This triggers the validation error because the <textarea> is a descendant of the <a> element.
✅ Valid: <textarea> and <a> as siblings
<div>
<textarea name="comment" rows="4" cols="40"></textarea>
<a href="/comments">View all comments</a>
</div>
Here, both elements live side by side inside a neutral <div>, avoiding any nesting conflict.
✅ Valid: <textarea> inside a <form> with a separate link
<form action="/submit-comment" method="post">
<label for="comment">Your comment:</label>
<textarea id="comment" name="comment" rows="4" cols="40"></textarea>
<button type="submit">Submit</button>
</form>
<a href="/comments">View all comments</a>
This is the most semantically correct approach when the textarea is part of a form—keep the form controls in a <form> and place any navigation links outside of it.
Other interactive elements to watch for
The same rule applies to other interactive content inside <a> elements. You also cannot nest <button>, <input>, <select>, <details>, or another <a> inside a link. If the validator reports a similar error for any of these elements, the fix follows the same principle: move the interactive element out of the anchor.
The <a> element with an href attribute is one of HTML’s most fundamental interactive elements. Browsers and assistive technologies inherently recognize it as a link — it’s focusable via the Tab key, activatable with Enter, and announced as “link” by screen readers. This built-in behavior is part of the element’s implicit ARIA role, which is link.
When you explicitly add role="link" to an <a href="..."> element, you’re telling assistive technologies something they already know. The W3C validator flags this as unnecessary because it violates the principle of not redundantly setting ARIA roles that match an element’s native semantics. This principle is codified in the first rule of ARIA use: “If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.”
While a redundant role="link" won’t typically break anything, it creates noise in your markup. It can also signal to other developers that the role is necessary, leading to confusion or cargo-cult patterns. Clean, semantic HTML that relies on native roles is easier to maintain and less error-prone.
The role="link" attribute is legitimately useful when a non-interactive element like a <span> or <div> needs to behave as a link. In that case, you must also manually implement keyboard interaction (focus via tabindex, activation via Enter key handling) and provide an accessible name. But when you already have a proper <a> element with href, all of that comes for free — no ARIA needed.
Examples
❌ Incorrect: redundant role="link" on an anchor
<a href="/about" role="link">About Us</a>
The role="link" is redundant here because the <a> element with href already has an implicit role of link.
✅ Correct: anchor without redundant role
<a href="/about">About Us</a>
Simply remove the role="link" attribute. The browser and assistive technologies already treat this as a link.
✅ Correct: using role="link" on a non-semantic element (when necessary)
<span role="link" tabindex="0" onclick="location.href='/about'" onkeydown="if(event.key==='Enter') location.href='/about'">
About Us
</span>
This is the legitimate use case for role="link" — when you cannot use a native <a> element and need to make a non-interactive element behave like a link. Note the additional work required: tabindex="0" for keyboard focusability, a click handler, and a keydown handler for Enter key activation. Using a proper <a> element avoids all of this extra effort.
❌ Incorrect: multiple anchors with redundant roles
<nav>
<a href="/" role="link">Home</a>
<a href="/products" role="link">Products</a>
<a href="/contact" role="link">Contact</a>
</nav>
✅ Correct: clean navigation without redundant roles
<nav>
<a href="/">Home</a>
<a href="/products">Products</a>
<a href="/contact">Contact</a>
</nav>
The longdesc attribute was originally designed to point to a URL containing a detailed description of an element’s content, primarily as an accessibility feature for screen reader users. On <iframe> elements, it was intended to describe the framed content for users who couldn’t perceive it directly. However, the attribute was poorly implemented across browsers, rarely used by assistive technologies, and was ultimately removed from the HTML specification for <iframe> elements.
Using obsolete attributes causes W3C validation errors and can create a false sense of accessibility. Since browsers and assistive technologies don’t reliably process longdesc on iframes, users who need the description may never actually reach it. A visible link, on the other hand, is universally accessible — it works for all users regardless of their browser, device, or assistive technology.
The fix is straightforward: remove the longdesc attribute from the <iframe> and place a regular <a> element near the iframe that links to the description page. This approach is more robust because the link is visible, discoverable, and works everywhere.
Examples
❌ Obsolete: Using longdesc on an <iframe>
<iframe src="report.html" title="Annual report" longdesc="report-description.html"></iframe>
This triggers the validation error because longdesc is no longer a valid attribute on <iframe>.
✅ Fixed: Using a visible link instead
<iframe src="report.html" title="Annual report"></iframe>
<p>
<a href="report-description.html">Read a detailed description of the annual report</a>
</p>
The longdesc attribute is removed, and a descriptive link is placed adjacent to the iframe so all users can access it.
✅ Alternative: Wrapping in a <figure> for better semantics
For a more structured approach, you can use a <figure> element with a <figcaption> to associate the description link with the iframe:
<figure>
<iframe src="report.html" title="Annual report"></iframe>
<figcaption>
Annual report overview.
<a href="report-description.html">Read the full description</a>.
</figcaption>
</figure>
This groups the iframe and its caption together semantically, making the relationship between them clear to both sighted users and assistive technologies.
✅ Alternative: Using aria-describedby for additional context
If you want to provide a short inline description that assistive technologies can announce automatically, you can use aria-describedby:
<p id="report-desc">
This iframe contains the interactive annual report for 2024.
<a href="report-description.html">Read the full description</a>.
</p>
<iframe src="report.html" title="Annual report" aria-describedby="report-desc"></iframe>
This links the iframe to the descriptive paragraph programmatically. Screen readers will announce the content of the referenced element when the iframe receives focus, while the visible link remains available to all users.
Key Takeaways
- Always include a title attribute on <iframe> elements to describe their purpose — this is an important baseline accessibility practice.
- Replace longdesc with a visible <a> element that links to the long description.
- Place the link near the iframe so users can easily find it.
- Consider using <figure> and <figcaption> or aria-describedby for stronger semantic association between the iframe and its description.
In older versions of HTML, the <a> element supported a shape attribute (with values like rect, circle, poly, and default) to define clickable hotspot regions within an image map. This feature was removed from the HTML specification, and the shape attribute is now considered obsolete on <a> elements.
The modern and correct way to create image maps is to use the <map> element containing one or more <area> elements. Each <area> element accepts a shape attribute along with coords to define clickable regions, and an href to specify the link destination. The <img> element is then associated with the map via its usemap attribute.
Why this matters
- Standards compliance: The shape attribute on <a> is not part of the current HTML living standard. Using it produces a validation error and relies on deprecated behavior that browsers are not required to support.
- Browser compatibility: Modern browsers implement image maps through <map> and <area>. Using the obsolete <a shape="..."> syntax may not work reliably across browsers.
- Accessibility: The <area> element is designed to work with assistive technologies in the context of image maps. It supports the alt attribute, which provides text alternatives for each clickable region — something essential for screen reader users.
How to fix it
- Remove the shape attribute from any <a> elements.
- Create a <map> element with a unique name attribute.
- Inside the <map>, add <area> elements with the appropriate shape, coords, href, and alt attributes.
- Associate the map with an <img> element using the usemap attribute, referencing the map’s name with a # prefix.
Examples
Incorrect: using shape on an <a> element
<img src="workspace.png" usemap="#workspace" alt="Workspace diagram" width="400" height="300">
<map name="workspace">
<a shape="rect" coords="0,0,200,150" href="/monitor.html">Monitor</a>
<a shape="circle" coords="300,200,50" href="/lamp.html">Desk lamp</a>
</map>
This triggers the validation error because the shape attribute is obsolete on <a> elements.
Correct: using <area> elements instead
<img src="workspace.png" usemap="#workspace" alt="Workspace diagram" width="400" height="300">
<map name="workspace">
<area shape="rect" coords="0,0,200,150" href="/monitor.html" alt="Monitor">
<area shape="circle" coords="300,200,50" href="/lamp.html" alt="Desk lamp">
</map>
Each <area> element defines a clickable region with shape and coords, links to a destination with href, and provides an accessible label with alt. The <area> element is a void element (no closing tag needed), and the alt attribute is required when href is present.
Supported shape values on <area>
| Value | Description | coords format |
|---|---|---|
| rect | A rectangle | x1,y1,x2,y2 |
| circle | A circle | centerX,centerY,radius |
| poly | A polygon defined by multiple points | x1,y1,x2,y2,...,xn,yn |
| default | The entire image area not covered by other shapes | No coords needed |
Example with multiple shape types
<img src="floorplan.png" usemap="#floorplan" alt="Office floor plan" width="600" height="400">
<map name="floorplan">
<area shape="rect" coords="10,10,200,150" href="/conference-room.html" alt="Conference room">
<area shape="circle" coords="400,300,60" href="/break-room.html" alt="Break room">
<area shape="poly" coords="300,10,350,80,250,80" href="/lobby.html" alt="Lobby">
<area shape="default" href="/office-overview.html" alt="General office area">
</map>
This example demonstrates all four shape types working together in a single image map, with proper alt text for each clickable region.
¿Listo para validar tus sitios?
Comienza tu prueba gratuita hoy.