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 defer and async boolean attributes control how and when an external script is fetched and executed relative to HTML parsing. These attributes exist specifically to optimize the loading of external resources. An inline <script> block (one without a src attribute) doesn't need to be "downloaded" — its content is already embedded in the HTML document. Because of this, the defer attribute has no meaningful effect on inline scripts, and the HTML specification explicitly forbids this combination.
According to the WHATWG HTML living standard, the defer attribute "must not be specified if the src attribute is not present." Browsers will simply ignore the defer attribute on inline scripts, which means the script will execute synchronously as if defer were never added. This can mislead developers into thinking their inline script execution is being deferred when it isn't, potentially causing subtle timing bugs that are difficult to diagnose.
The same rule applies to the async attribute — it also requires the presence of a src attribute to be valid.
How to fix it
You have two options depending on your situation:
- If the script should be deferred, move the inline code into an external
.jsfile and reference it with thesrcattribute alongsidedefer. - If the script must remain inline, remove the
deferattribute entirely. If you need deferred execution for inline code, consider placing the<script>element at the end of the<body>, or useDOMContentLoadedto wait for the document to finish parsing.
Examples
❌ Invalid: defer on an inline script
<scriptdefer>
console.log("hello");
</script>
This triggers the validation error because defer is present without a corresponding src attribute.
✅ Fix option 1: Add a src attribute
Move the JavaScript into an external file (e.g., app.js) and reference it:
<scriptdefersrc="app.js"></script>
The browser will download app.js in parallel with HTML parsing and execute it only after the document is fully parsed.
✅ Fix option 2: Remove defer from the inline script
If the script must stay inline, simply remove the defer attribute:
<script>
console.log("hello");
</script>
✅ Fix option 3: Use DOMContentLoaded for deferred inline execution
If you need your inline script to wait until the DOM is ready, wrap the code in a DOMContentLoaded event listener:
<script>
document.addEventListener("DOMContentLoaded",function(){
console.log("DOM is fully parsed");
});
</script>
This achieves a similar effect to defer but is valid for inline scripts.
❌ Invalid: async on an inline script
The same rule applies to async:
<scriptasync>
document.title="Updated";
</script>
✅ Fixed
<scriptasyncsrc="update-title.js"></script>
Or simply remove async if the script is inline:
<script>
document.title="Updated";
</script>
The aria-expanded attribute requires the element to have an appropriate role attribute (or be an element that natively implies one). A <span> is a generic inline element with no implicit ARIA role, so you must explicitly assign a role when using ARIA state attributes like aria-expanded.
The aria-expanded attribute indicates whether a grouping element controlled by this element is currently expanded or collapsed. It is only valid on elements with specific roles such as button, link, combobox, menuitem, or other widget roles. When a <span> uses aria-expanded without a role, validators flag it because there's no semantic context for that state.
Since this element toggles a dropdown menu and has aria-label, aria-controls, and aria-expanded, the most appropriate role is button. This tells assistive technologies that the element is interactive and can be activated.
Also note that when using role="button" on a non-interactive element like <span>, you should ensure it is focusable by adding tabindex="0" and that it handles keyboard events (Enter and Space keys).
HTML Examples
❌ Invalid: aria-expanded on a span without a role
<spanclass="navbar-dropdown-icon"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</span>
✅ Valid: adding role="button" and tabindex="0"
<spanclass="navbar-dropdown-icon"
role="button"
tabindex="0"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</span>
✅ Better: use a <button> element instead
<buttonclass="navbar-dropdown-icon"
type="button"
aria-expanded="false"
aria-label="List options"
aria-controls="dropdown-menu-item-1-1menu-item-2-6"
data-toggle="dropdown">
</button>
Using a native <button> is preferred because it is focusable and keyboard-accessible by default, without needing role or tabindex.
The span element is a generic inline container with no inherent semantics. On its own, it carries no meaning for assistive technologies. When you add ARIA attributes like aria-expanded or aria-valuenow to a span, you are signaling that the element represents an interactive widget — but the validator (and assistive technologies) need more context. Many ARIA attributes are only permitted on elements that have certain roles, and some roles require a specific set of attributes to function correctly.
For example, aria-valuenow is designed for range widgets like sliders and progress bars. According to the WAI-ARIA specification, if you use aria-valuenow, the element must also have aria-valuemin, aria-valuemax, and a role such as progressbar, slider, meter, or scrollbar. Similarly, aria-expanded is meant for elements with roles like button, combobox, link, or treeitem. Placing these attributes on a bare span without the corresponding role violates the ARIA rules and triggers this validation error.
This matters for several reasons:
- Accessibility: Screen readers rely on the
roleto determine how to present a widget to users. Without it, ARIA state attributes become meaningless or confusing. - Standards compliance: The HTML specification integrates ARIA rules, and validators enforce that ARIA attributes are used in valid combinations.
- Browser behavior: Browsers use the
roleto build the accessibility tree. Aspanwitharia-valuenowbut norolemay be ignored or misrepresented to assistive technology users.
How to fix it
- Add the correct
roleto thespan, along with all attributes required by that role. - Use a semantic HTML element instead of a
spanwhen one exists (e.g.,<progress>or<button>). - Remove unnecessary ARIA attributes if the
spanis purely decorative or the attributes were added by mistake.
If your span is purely visual (e.g., a decorative asterisk for required fields), don't add state-related ARIA attributes to it. Instead, use aria-hidden="true" to hide it from assistive technologies, and place ARIA attributes on the actual form control.
Examples
Incorrect: aria-expanded on a span without a role
<spanaria-expanded="false">Menu</span>
The validator reports the missing role because aria-expanded isn't valid on a generic span.
Correct: Add a role (or use a button)
<spanrole="button"tabindex="0"aria-expanded="false">Menu</span>
Or, better yet, use a real button element:
<buttonaria-expanded="false">Menu</button>
Incorrect: aria-valuenow without the full set of range attributes and role
<spanclass="progress-indicator"aria-valuenow="50">50%</span>
Correct: Include the role and all required range attributes
<spanrole="progressbar"aria-valuenow="50"aria-valuemin="0"aria-valuemax="100">
50%
</span>
Or use the native <progress> element, which has built-in semantics:
<progressvalue="50"max="100">50%</progress>
Incorrect: aria-required on a decorative span
<labelfor="email">
<spanclass="required"aria-required="true">*</span>
</label>
<inputid="email"name="email"type="email">
The aria-required attribute belongs on the form control, not on the decorative asterisk.
Correct: Hide the decorative indicator and mark the input as required
<labelfor="email">
<spanclass="required"aria-hidden="true">*</span>
</label>
<inputid="email"name="email"type="email"aria-required="true">
If you also want screen readers to announce "required" as part of the label text, add visually hidden text:
<labelfor="email">
<spanaria-hidden="true">*</span>
<spanclass="visually-hidden">required</span>
</label>
<inputid="email"name="email"type="email"required>
The key takeaway: whenever you use ARIA state or property attributes on a span, make sure the element also has the correct role and all companion attributes required by that role. When a native HTML element already provides the semantics you need — such as <button>, <progress>, or <meter> — prefer it over a span with ARIA, as native elements are more robust and require less additional markup.
The summary element needs an explicit role attribute when the W3C validator detects it's being used in a context where its implicit ARIA semantics are unclear or overridden.
The summary element is designed to be used as the first child of a <details> element, where it acts as a clickable disclosure toggle. When used correctly inside <details>, it has an implicit ARIA role and doesn't need additional attributes.
This validation warning typically appears when:
- The
summaryelement is used outside of a<details>element. - The
summaryelement has an explicitroleattribute that requires additional ARIA properties (e.g.,role="checkbox"requiresaria-checked, orrole="heading"requiresaria-level).
The simplest fix is to ensure summary is used correctly as a direct child of <details>, and to remove any unnecessary or conflicting role attributes.
Example with the issue
<!-- summary outside of details triggers the warning -->
<summary>Click to expand</summary>
<p>Some content here.</p>
<!-- Or summary with an incomplete role override -->
<details>
<summaryrole="heading">Section Title</summary>
<p>Some content here.</p>
</details>
How to fix it
<!-- Use summary correctly inside details -->
<details>
<summary>Click to expand</summary>
<p>Some content here.</p>
</details>
<!-- If you need a heading role, include the required aria-level -->
<details>
<summaryrole="heading"aria-level="3">Section Title</summary>
<p>Some content here.</p>
</details>
If you don't have a specific reason to override the role, simply remove the role attribute and let the summary element keep its native semantics within <details>.
The aria-checked attribute communicates the checked state of an interactive widget to assistive technologies. According to the WAI-ARIA specification, this attribute is only permitted on elements that have a role supporting the "checked" state — such as checkbox, switch, radio, menuitemcheckbox, or menuitemradio. A plain <td> element has an implicit role of cell (or gridcell when inside a role="grid" table), neither of which supports aria-checked. When the validator encounters aria-checked on a <td> without a compatible role, it flags the element as invalid.
This matters for several reasons:
- Accessibility: Screen readers and other assistive technologies rely on the relationship between
roleand ARIA state attributes. Anaria-checkedon an element without a recognized checkable role creates a confusing or broken experience — users may not understand that the cell is supposed to be interactive. - Standards compliance: The ARIA in HTML specification defines strict rules about which attributes are allowed on which roles. Violating these rules means your HTML is technically invalid.
- Browser behavior: Browsers may ignore
aria-checkedentirely when it's used on an element without a valid role, making the attribute useless.
How to fix it
You have two main approaches depending on what your <td> is meant to do:
1. Add an appropriate role attribute. If the table cell genuinely represents a checkable control (for example, in an interactive data grid), add role="checkbox", role="switch", or another appropriate checkable role to the <td>, along with tabindex for keyboard accessibility.
2. Remove aria-checked and use a real control. If the cell simply contains a checkbox or toggle, place an actual <input type="checkbox"> inside the <td> and remove the ARIA attributes from the cell itself. Native HTML controls already communicate their state to assistive technologies without extra ARIA.
Examples
❌ Incorrect: aria-checked without a role
<table>
<tr>
<tdaria-checked="true">Selected</td>
<td>Item A</td>
</tr>
</table>
This triggers the error because <td> has the implicit role of cell, which does not support aria-checked.
✅ Fix: Add a compatible role to the <td>
<tablerole="grid">
<tr>
<tdrole="checkbox"aria-checked="true"tabindex="0">Selected</td>
<td>Item A</td>
</tr>
</table>
Here the <td> explicitly has role="checkbox", which supports aria-checked. The tabindex="0" makes it keyboard-focusable, and role="grid" on the table signals that cells may be interactive.
✅ Fix: Use a native checkbox inside the <td>
<table>
<tr>
<td>
<label>
<inputtype="checkbox"checked>
Selected
</label>
</td>
<td>Item A</td>
</tr>
</table>
This approach is often the best option. The native <input type="checkbox"> already conveys its checked state to assistive technologies, and no ARIA attributes are needed on the <td>.
❌ Incorrect: Mismatched role and aria-checked
<table>
<tr>
<tdrole="button"aria-checked="false">Toggle</td>
<td>Item B</td>
</tr>
</table>
The button role does not support aria-checked. This would trigger a different but related validation error.
✅ Fix: Use a role that supports aria-checked
<tablerole="grid">
<tr>
<tdrole="switch"aria-checked="false"tabindex="0">Toggle</td>
<td>Item B</td>
</tr>
</table>
The switch role supports aria-checked and is appropriate for toggle-style controls.
The HTML specification requires every document to have a <title> element with at least one non-whitespace character. The title serves as the primary label for the page — it appears in the browser tab, in bookmarks, in search engine results, and is announced by screen readers when a user navigates to the page. An empty title leaves users with no way to identify or distinguish the page, which is especially problematic for accessibility. Screen reader users rely on the document title to understand what page they've landed on, and an empty title provides no context at all.
Browsers may attempt to display something (such as the URL) when the title is missing or empty, but this fallback is inconsistent and often produces a poor user experience. Search engines also depend on the <title> element for indexing and displaying results, so an empty title can negatively affect discoverability.
This error commonly occurs when templates or boilerplate code include a <title> tag as a placeholder that never gets filled in, or when content management systems fail to inject a title value into the template.
How to fix it
Add descriptive, concise text inside the <title> element that accurately reflects the content of the page. A good title is typically 20–70 characters long and specific enough to distinguish the page from other pages on the same site.
- Every page should have a unique title relevant to its content.
- Avoid generic titles like "Untitled" or "Page" — be specific.
- For multi-page sites, consider a format like "Page Name - Site Name".
Examples
❌ Empty title element
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title></title>
</head>
<body>
<p>Welcome to our website.</p>
</body>
</html>
This triggers the error because the <title> element contains no text.
❌ Title with only whitespace
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title></title>
</head>
<body>
<p>Welcome to our website.</p>
</body>
</html>
This also triggers the error. Whitespace-only content is treated as empty.
✅ Title with descriptive text
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>Getting Started - Automated Website Validator</title>
</head>
<body>
<p>Welcome to our website.</p>
</body>
</html>
The <title> element now contains meaningful text that identifies both the page and the site, making it accessible, SEO-friendly, and standards-compliant.
ARIA (Accessible Rich Internet Applications) works as a system where roles define what an element is, and states and properties describe the element's current condition or characteristics. Certain ARIA attributes are only valid when used on elements that have a specific role — either explicitly declared via the role attribute or implicitly provided by the HTML element itself. When you add an ARIA state or property to a generic element like a <div> or <span> without specifying a role, assistive technologies have no context for interpreting that attribute. For example, aria-expanded="true" on a plain <div> tells a screen reader that something is expanded, but it doesn't communicate what is expanded — is it a button, a navigation menu, a tree item? The role provides that crucial context.
This matters for several reasons:
- Accessibility: Screen readers and other assistive technologies rely on the combination of roles and their associated states/properties to convey meaningful information to users. An ARIA property without a role is ambiguous and can lead to a confusing experience.
- Standards compliance: The WAI-ARIA specification defines which states and properties are allowed on which roles. Using an ARIA attribute outside of a valid role context violates the spec.
- Predictable behavior: Browsers and assistive technologies may handle orphaned ARIA attributes inconsistently, leading to unpredictable results across different platforms.
To fix this issue, you have two approaches:
- Add an explicit
roleattribute to the element, choosing a role that supports the ARIA attributes you're using. - Use a semantic HTML element that already has an implicit ARIA role. For instance,
<nav>has an implicit role ofnavigation,<button>has an implicit role ofbutton, and<header>has an implicit role ofbanner. This is generally the preferred approach, as it provides built-in keyboard interaction and semantics without extra effort.
When choosing a role, make sure the ARIA states and properties you're using are actually supported by that role. For example, aria-expanded is supported by roles like button, combobox, link, treeitem, and others — but not by every role. Consult the WAI-ARIA roles documentation to verify compatibility.
Examples
Invalid: ARIA property without a role
This <div> uses aria-expanded but has no role, so the validator doesn't know what kind of element this is supposed to be.
<divaria-expanded="true">
Menu contents
</div>
Fixed: Adding an explicit role
Adding role="button" tells assistive technologies that this is a button that can be expanded or collapsed.
<divrole="button"aria-expanded="true"tabindex="0">
Menu contents
</div>
Fixed: Using a semantic HTML element instead
A <button> element already has an implicit button role, so no explicit role attribute is needed. This is the preferred approach.
<buttonaria-expanded="true">
Toggle menu
</button>
Invalid: aria-label on a generic element
A <span> has no implicit role, so aria-label has no meaningful context here.
<spanaria-label="Close dialog">X</span>
Fixed: Using a semantic element or adding a role
<buttonaria-label="Close dialog">X</button>
Or, if you need to use a <span>:
<spanrole="button"tabindex="0"aria-label="Close dialog">X</span>
Using elements with implicit roles
Many HTML elements already carry implicit ARIA roles, so adding ARIA states and properties to them is valid without an explicit role attribute:
<!-- <nav> has implicit role="navigation" -->
<navaria-label="Main navigation">
<ul>
<li><ahref="/">Home</a></li>
<li><ahref="/about">About</a></li>
</ul>
</nav>
<!-- <details> supports aria-expanded implicitly -->
<detailsaria-describedby="help-text">
<summary>More information</summary>
<pid="help-text">Additional details about this topic.</p>
</details>
As a general rule, always prefer native semantic HTML elements over generic elements with ARIA roles. Native elements come with built-in keyboard support, focus management, and accessibility semantics — reducing the amount of custom code you need to write and maintain.
Required attributes exist because they provide information that is fundamental to the element's purpose. For example, an <img> element without a src attribute has no image to display, and without an alt attribute, assistive technologies have no fallback text to describe the image. A <link> element without rel gives the browser no way to understand the relationship of the linked resource. When you omit a required attribute, several things can go wrong:
- Accessibility issues: Screen readers and other assistive technologies depend on specific attributes to convey meaning. A missing
alton<img>leaves visually impaired users without any description of the content. - Broken functionality: Some elements simply won't work without their required attributes. A
<form>with an<input>that's missing atypeattribute may still render (browsers default totype="text"), but other elements like<map>withoutnamewon't function as intended. - Standards non-compliance: Omitting required attributes produces invalid HTML, which can lead to unpredictable rendering across different browsers.
To fix this issue, identify which attribute is missing from the element flagged by the validator, then add it with an appropriate value. Here are common elements and their required attributes:
| Element | Required Attribute(s) |
|---|---|
<img> | src, alt |
<link> | rel |
<script> | src (if no inline content) |
<input> | type (recommended) |
<meta> | charset, name, or http-equiv (at least one) |
<map> | name |
<bdo> | dir |
<style> | None in HTML5, but type was required in HTML4 |
Examples
Missing alt attribute on <img>
This is one of the most common validation errors. The alt attribute is required on every <img> element.
❌ Incorrect — missing alt attribute:
<imgsrc="photo.jpg">
✅ Correct — alt attribute provided:
<imgsrc="photo.jpg"alt="A sunset over the ocean">
If the image is purely decorative and carries no meaningful content, use an empty alt attribute:
<imgsrc="decorative-border.png"alt="">
Missing rel attribute on <link>
The rel attribute tells the browser what the linked resource is for.
❌ Incorrect — missing rel attribute:
<linkhref="styles.css">
✅ Correct — rel attribute provided:
<linkrel="stylesheet"href="styles.css">
Missing name attribute on <map>
The <map> element requires a name attribute so it can be referenced by an <img> element's usemap attribute.
❌ Incorrect — missing name attribute:
<map>
<areashape="rect"coords="0,0,100,100"href="/section"alt="Section">
</map>
✅ Correct — name attribute provided:
<mapname="navigation">
<areashape="rect"coords="0,0,100,100"href="/section"alt="Section">
</map>
<imgsrc="nav.png"alt="Site navigation"usemap="#navigation">
Missing dir attribute on <bdo>
The <bdo> (bidirectional override) element exists specifically to override text direction, so the dir attribute is essential.
❌ Incorrect — missing dir attribute:
<p>The word in Arabic is <bdo>مرحبا</bdo>.</p>
✅ Correct — dir attribute provided:
<p>The word in Arabic is <bdodir="rtl">مرحبا</bdo>.</p>
When you encounter this validation error, read the validator message carefully — it will tell you exactly which element and which attribute is missing. Add the attribute with a meaningful value appropriate to your content, and revalidate to confirm the issue is resolved.
HTML elements follow strict nesting rules defined by the WHATWG HTML Living Standard. Every element has a content model — a description of what content (text, elements, or both) it may contain. When you place an element somewhere it isn't allowed, the browser must guess your intent and may restructure the DOM in unexpected ways. This can lead to inconsistent rendering across browsers, broken layouts, and accessibility issues for screen readers and other assistive technologies.
The "(Suppressing further errors from this subtree.)" part of the message means the validator has stopped checking anything nested inside the problematic element. This is important — it means there could be additional errors hidden beneath this one. Fixing this nesting issue may reveal further problems that need attention.
Here are some of the most common cases that trigger this error:
- Block elements inside inline elements: Placing a
<div>inside a<span>or an<a>that doesn't permit flow content in that context. - Invalid list children: Putting
<div>,<p>, or other elements directly inside<ul>or<ol>, which only allow<li>(and<script>/<template>) as direct children. - Invalid table structure: Placing
<td>directly inside<table>without wrapping it in<tr>, or putting non-table elements where only<thead>,<tbody>,<tfoot>, or<tr>are expected. - Headings or paragraphs inside
<p>: The<p>element only accepts phrasing content, so nesting a<h2>or another<p>inside it is invalid. - Interactive elements inside interactive elements: Nesting a
<button>inside an<a>, or an<a>inside a<button>.
To fix the issue, consult the MDN documentation for the parent element and check its Permitted content section. Then either move the child element to a valid location, wrap it in an appropriate intermediary element, or replace the parent or child with a more suitable element.
Examples
Invalid list children
A <ul> only permits <li> elements as direct children.
❌ Incorrect:
<ul>
<div>
<li>Item one</li>
<li>Item two</li>
</div>
</ul>
✅ Correct:
<ul>
<li>Item one</li>
<li>Item two</li>
</ul>
Block element inside a paragraph
The <p> element only accepts phrasing content. A <div> is flow content and cannot be nested inside it.
❌ Incorrect:
<p>
Here is some text.
<divclass="highlight">This is highlighted.</div>
</p>
✅ Correct:
<p>Here is some text.</p>
<divclass="highlight">This is highlighted.</div>
Or use a <span> if you want inline styling within the paragraph:
<p>
Here is some text.
<spanclass="highlight">This is highlighted.</span>
</p>
Invalid table structure
Table cells (<td>) must be inside a <tr>, which itself must be inside <thead>, <tbody>, <tfoot>, or directly inside <table>.
❌ Incorrect:
<table>
<td>Name</td>
<td>Age</td>
</table>
✅ Correct:
<table>
<tr>
<td>Name</td>
<td>Age</td>
</tr>
</table>
Interactive elements nested inside interactive elements
A <button> cannot be placed inside an <a> element, and vice versa, because interactive content cannot nest inside other interactive content.
❌ Incorrect:
<ahref="/dashboard">
<button>Go to Dashboard</button>
</a>
✅ Correct (choose one or the other):
<ahref="/dashboard">Go to Dashboard</a>
Or style a link to look like a button using CSS:
<ahref="/dashboard"class="button">Go to Dashboard</a>
Wrapping elements inside <select>
The <select> element only allows <option>, <optgroup>, and scripting elements as children.
❌ Incorrect:
<select>
<div>
<option>Apple</option>
<option>Banana</option>
</div>
</select>
✅ Correct:
<select>
<optgrouplabel="Fruits">
<option>Apple</option>
<option>Banana</option>
</optgroup>
</select>
Headings play a critical role in structuring a web page. They create an outline of the document that both users and machines rely on. Screen readers, for example, allow users to navigate a page by jumping between headings, making them one of the most important tools for accessible navigation. When a heading is empty, screen readers may announce "heading level 2" (or similar) with no accompanying text, leaving users confused about what section they've entered.
Empty headings also signal a structural problem. They often appear when developers use heading elements purely for spacing or styling purposes, when content is meant to be injected dynamically via JavaScript but the script fails, or when headings are left as placeholders during development and never filled in.
The W3C validator flags this as a warning because the HTML specification states that headings represent the topic of their section. An empty heading cannot fulfill this purpose. While it is technically parseable HTML, it is semantically meaningless and degrades the quality of the document.
How to fix it
- Add descriptive text to the heading that accurately represents the content of its section.
- Remove the empty heading if it serves no purpose. Don't keep empty headings as spacers — use CSS margins or padding instead.
- If content is loaded dynamically, consider adding the heading element via JavaScript at the same time as its content, rather than leaving an empty shell in the markup.
- If you're hiding the heading visually but still want it available for screen readers, use a visually-hidden CSS technique rather than leaving it empty.
Examples
❌ Empty headings (triggers the warning)
<h1></h1>
<h2></h2>
<h3>
</h3>
All three of these are considered empty — even the ones containing whitespace or a newline.
❌ Using an empty heading for spacing
<h2></h2>
<p>This paragraph needs some space above it.</p>
This misuses a heading element for visual layout purposes.
✅ Heading with meaningful content
<h1>Welcome to Our Store</h1>
<h2>Featured Products</h2>
<p>Check out our latest arrivals.</p>
✅ Using CSS for spacing instead of empty headings
<pclass="section-intro">This paragraph needs some space above it.</p>
.section-intro{
margin-top:2rem;
}
✅ Visually hidden heading for screen readers
If you need a heading that is available to assistive technologies but not visible on screen, include text and hide it with CSS:
<h2class="visually-hidden">Navigation Menu</h2>
<nav>
<ul>
<li><ahref="/">Home</a></li>
<li><ahref="/about">About</a></li>
</ul>
</nav>
.visually-hidden{
position: absolute;
width:1px;
height:1px;
padding:0;
margin:-1px;
overflow: hidden;
clip:rect(0,0,0,0);
white-space: nowrap;
border:0;
}
✅ Adding headings dynamically with their content
Instead of placing an empty heading in the HTML and populating it later, create the heading along with its content:
<divid="results"></div>
<script>
constcontainer=document.getElementById("results");
constheading=document.createElement("h2");
heading.textContent="Search Results";
container.appendChild(heading);
</script>
This approach avoids any moment where an empty heading exists in the DOM, ensuring validity and accessibility at all times.
When the browser's HTML parser reaches the end of the file, it expects all opened elements to have been closed. If they haven't, the validator flags each unclosed element individually. While browsers will attempt to auto-close these elements using error recovery rules, the results can be unpredictable — different browsers may interpret the intended structure differently, leading to inconsistent rendering, broken layouts, and unexpected behavior.
This error is especially problematic for accessibility. Screen readers and other assistive technologies rely on a well-formed document tree to convey page structure to users. Unclosed elements can create a malformed DOM where content ends up nested inside the wrong parent, making navigation confusing or impossible for people using assistive devices.
There are several common causes for this error:
- Missing closing tags — Forgetting to write
</div>,</section>,</p>, or similar end tags. - Missing
</body>or</html>— The document's outermost structural tags are left unclosed, often because the bottom of the file was truncated or accidentally deleted. - Mismatched nesting — Closing tags appear in the wrong order, which can cause the parser to treat elements as still open. For example,
<b><i></b></i>closes<b>before<i>, leaving<i>effectively unclosed from the parser's perspective. - Typos in closing tags — Writing
</dev>instead of</div>, which the parser doesn't recognize as a valid closing tag for the open element. - Template or build errors — Server-side templates, component frameworks, or build tools sometimes produce partial HTML output that is missing closing tags.
To fix this issue, work through your document methodically. Use your code editor's bracket matching or tag highlighting features to pair each start tag with its end tag. Proper indentation makes this much easier — when nested elements are consistently indented, a missing closing tag becomes visually obvious because the indentation levels won't line up.
Examples
Missing closing tags throughout the document
This document has multiple unclosed elements, including the structural <footer>, <body>, and <html> tags:
<!-- ❌ Triggers the error -->
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>Sample Page</title>
</head>
<body>
<div>
<h1>Welcome to My Page
<p>This is a paragraph in a div.
</div>
<footer>
<p>Footer content here
The validator will report open elements for <h1>, the <p> tags, <footer>, <body>, and <html>. Here is the corrected version:
<!-- ✅ All elements properly closed -->
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>Sample Page</title>
</head>
<body>
<div>
<h1>Welcome to My Page</h1>
<p>This is a paragraph in a div.</p>
</div>
<footer>
<p>Footer content here</p>
</footer>
</body>
</html>
Mismatched nesting order
Even when all closing tags are technically present, the wrong order can trigger this error:
<!-- ❌ Tags closed in the wrong order -->
<div>
<section>
<p>Some content</p>
</div>
</section>
The </div> appears before </section>, but <section> is nested inside <div>. The parser closes <div> first, and because <section> was opened inside it, the explicit </section> that follows doesn't match anything correctly. Fix this by closing elements in reverse order of how they were opened:
<!-- ✅ Correct nesting order -->
<div>
<section>
<p>Some content</p>
</section>
</div>
Typo in a closing tag
A subtle typo can leave an element open without any obvious visual clue:
<!-- ❌ Typo: </setion> instead of </section> -->
<section>
<p>Article content</p>
</setion>
The parser doesn't recognize </setion> as the closing tag for <section>, so <section> remains open. Correct the spelling:
<!-- ✅ Properly spelled closing tag -->
<section>
<p>Article content</p>
</section>
Void elements mistakenly given closing tags that disrupt nesting
Sometimes adding a closing tag to a void element like <input> or <br> can confuse the parser and cause downstream nesting issues. Void elements must not have closing tags:
<!-- ❌ Closing tag on a void element can cause confusion -->
<div>
<inputtype="text"></input>
</div>
<!-- ✅ Void elements are self-closing; no end tag needed -->
<div>
<inputtype="text">
</div>
Tips for preventing this issue
- Indent consistently — Use two or four spaces for each nesting level. When every child element is indented one level deeper than its parent, missing closing tags become easy to spot.
- Use editor features — Most code editors offer tag auto-closing, bracket matching, and tag pair highlighting. Enable these to catch mistakes as you type.
- Validate early and often — Run your HTML through the W3C validator during development, not just at the end. This catches unclosed elements before they cascade into dozens of related errors.
- Check complex structures carefully — Tables, forms, nested lists, and multi-level layouts have many nested elements. These are the most common places where a closing tag gets accidentally omitted or misplaced.
- Review template output — If you use server-side templates or a JavaScript framework that generates HTML, validate the rendered output, not just the source template. Conditional logic in templates is a frequent source of missing closing tags.
An HTML element was opened but never closed before the document ended, meaning the parser reached the end of the file while still expecting content or a closing tag.
This error occurs when there is a mismatch between opening and closing tags in your document. Common causes include a missing closing tag (like </div>, </body>, or </html>), an extra opening tag, or accidentally deleted lines. The HTML parser keeps track of every element it opens, and when it reaches the end of the file with unclosed elements still on its stack, it reports this error.
The best way to track this down is to check that every opening tag has a corresponding closing tag, paying special attention to deeply nested structures. Look for missing </div>, </section>, </main>, </body>, or </html> tags — these are the most frequently forgotten.
HTML Examples
❌ Broken: missing closing tags
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<divclass="wrapper">
<main>
<p>Hello world</p>
</main>
In this example, the </div>, </body>, and </html> closing tags are all missing. The parser reaches the end of the file while still expecting them.
✅ Fixed: all tags properly closed
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<divclass="wrapper">
<main>
<p>Hello world</p>
</main>
</div>
</body>
</html>
A good habit is to write both the opening and closing tags at the same time, then fill in the content between them. Most code editors also offer auto-closing and bracket-matching features that help prevent this issue.
Every HTML document must begin with a Document Type Declaration (doctype). The doctype tells browsers which version and standard of HTML the document uses, ensuring they render the page in standards mode rather than quirks mode. Without it, browsers fall back to quirks mode, which emulates legacy rendering behavior from the early web — leading to inconsistent layouts, unexpected CSS behavior, and subtle bugs that differ across browsers.
Since HTML5, the doctype is simply <!DOCTYPE html>. It is case-insensitive (<!doctype html> also works), but it must appear before any other content in the document, including the <html> tag. Even a blank line or whitespace before the doctype can sometimes cause issues in older browsers.
This error can be triggered by several common scenarios:
- The doctype is completely missing. The file jumps straight into
<html>or other markup. - The doctype is misspelled. For example,
<!DOCKTYPE html>or<!DOCTYPE hmtl>. - The file is empty or contains only non-HTML content. The validator reached the end without finding any valid HTML structure.
- A Byte Order Mark (BOM) or invisible characters appear before the doctype, which can happen when files are saved with certain text editors.
- The doctype is placed after other elements, such as a comment or
<html>tag.
To fix this, ensure <!DOCTYPE html> is the very first line of your file, with no preceding content.
Examples
❌ Missing doctype entirely
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
This triggers the error because the validator never encounters a doctype declaration.
❌ Misspelled doctype
<!DOCKTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The typo DOCKTYPE means the validator does not recognize it as a valid doctype.
❌ Doctype placed after the <html> tag
<htmllang="en">
<!DOCTYPE html>
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The doctype must come before the <html> tag, not after it.
✅ Correct minimal HTML5 document
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The <!DOCTYPE html> declaration appears on the very first line, followed by the <html> tag with a lang attribute, a <head> containing a <title>, and a <body> with the page content. This is the minimal structure for a valid HTML5 document.
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>
<ahref="one.html">Page 1
<ahref="two.html">Page 2</a>
</nav>
✅ Fixed by adding the missing closing tag
<nav>
<ahref="one.html">Page 1</a>
<ahref="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:
<ahref="/article">
<h2>Article Title</h2>
<p>A short summary of the article.</p>
<ahref="/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>
<ahref="/article">
<h2>Article Title</h2>
<p>A short summary of the article.</p>
</a>
<p>By <ahref="/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><ahref="/home">Home</li>
<li><ahref="/about">About</a></li>
<li><ahref="/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><ahref="/home">Home</a></li>
<li><ahref="/about">About</a></li>
<li><ahref="/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.
HTML follows strict nesting rules: when one element is opened inside another, the inner element must be closed before the outer one. This is sometimes called the "last opened, first closed" principle. When a </b> end tag appears while another element that was opened inside the <b> is still open, the validator reports this nesting violation.
This matters for several reasons. First, browsers must guess what you intended when they encounter improperly nested tags, and different browsers may interpret the structure differently, leading to inconsistent rendering. Second, assistive technologies like screen readers rely on a well-formed DOM tree to convey the correct meaning and structure of content to users. Misnested tags can produce a confusing or broken experience. Third, improperly nested elements can cause unexpected behavior with CSS styling, since the computed DOM tree may not match what you wrote in your source code.
The fix is straightforward: always close elements in the exact reverse order you opened them. If <b> is opened first and <a> is opened second, then </a> must come before </b>.
Examples
Incorrect: <b> closed before a nested <a>
<p><b><ahref="/about">About us</b></a></p>
Here, the <a> element was opened inside the <b>, but </b> appears before </a>. This violates the nesting rules.
Correct: inner element closed first
<p><b><ahref="/about">About us</a></b></p>
The <a> is closed first, then the <b>, respecting the order in which they were opened.
Incorrect: multiple nesting violations
<p><b><em><ahref="#">Click here</b></em></a></p>
Three elements are opened (<b>, then <em>, then <a>), but they are closed in the wrong order.
Correct: proper reverse-order closing
<p><b><em><ahref="#">Click here</a></em></b></p>
The elements close in reverse order: </a> first, then </em>, then </b>.
Incorrect: <b> overlapping with a block-level element
<b><p>Bold paragraph</b></p>
Beyond the nesting order issue, note that <b> is a phrasing (inline) element and should not wrap <p> (a flow/block element). This is a separate but related violation.
Correct: <b> inside the paragraph
<p><b>Bold paragraph</b></p>
The <b> element is now properly placed inside the <p>, and the nesting order is correct.
A helpful mental model
Think of HTML nesting like parentheses in math. Every opening must have a corresponding close, and they cannot cross:
Wrong: ( [ ) ]
Right: ( [ ] )
Translating to HTML:
<!-- Wrong -->
<b><em>text</b></em>
<!-- Right -->
<b><em>text</em></b>
If you are working with complex or deeply nested markup, using a code editor with auto-closing tags and bracket matching can help you spot these issues before they reach the validator.
In HTML, certain elements are classified as void elements — they cannot contain any content and must not have a closing (end) tag. The <br> element, which represents a line break, is one of these void elements. Others include <img>, <input>, <hr>, <meta>, and <link>.
When the validator encounters </br>, it interprets it as a closing tag for a <br> element. Since void elements are forbidden from having closing tags by the HTML specification, this produces the error "End tag br."
Why this matters
- Standards compliance: The WHATWG HTML Living Standard explicitly states that void elements must not have an end tag. Using
</br>violates this rule. - Browser inconsistency: While most browsers will silently recover from
</br>— some treat it as a<br>, others may ignore it entirely — relying on error recovery behavior is unpredictable and can lead to inconsistent rendering across browsers. - Code clarity: Using
</br>suggests the element has an opening and closing pair, which is misleading to other developers reading the code. It implies a misunderstanding of how the element works.
How to fix it
Replace every instance of </br> with <br>. That's it. There's no need for a closing tag because <br> is self-closing by definition.
Both <br> and <br/> (with a trailing slash) are valid in HTML5. The <br> form is generally preferred in HTML documents, while <br/> is required in XHTML and sometimes used for compatibility with XML-based tools.
Examples
❌ Invalid: using an end tag for <br>
<p>First line</br>Second line</p>
This triggers the "End tag br" validation error.
❌ Also invalid: pairing an opening and closing <br> tag
<p>First line<br></br>Second line</p>
Even when paired with an opening <br>, the </br> end tag is still invalid.
✅ Valid: using <br> without a closing tag
<p>First line<br>Second line</p>
✅ Also valid: self-closing syntax with a trailing slash
<p>First line<br/>Second line</p>
This form is acceptable in HTML5, though <br> without the slash is more conventional in modern HTML.
✅ Practical example: an address block
<address>
123 Main Street<br>
Suite 400<br>
Springfield, IL 62704
</address>
Other void elements
The same rule applies to all void elements. None of these should have closing tags:
<area>, <base>, <br>, <col>, <embed>, <hr>, <img>, <input>, <link>, <meta>, <source>, <track>, <wbr>
If you see a similar "End tag" error for any of these elements, the fix is the same: remove the closing tag.
The <code> element is an inline-level (phrasing content) element designed to represent a fragment of computer code. According to the HTML specification, it can only contain other phrasing content — elements like <span>, <em>, <strong>, <a>, and text nodes. It cannot contain block-level (flow content) elements such as <div>, <p>, <ul>, <h1>–<h6>, or <table>.
When the validator reports that an end tag </code> violates nesting rules, it means one of two things is happening:
Disallowed content inside
<code>: A block-level element has been placed inside the<code>element. When the browser encounters a block-level element where only phrasing content is allowed, it may implicitly close the<code>element. The subsequent</code>end tag then has no matching open tag, causing the nesting violation.Overlapping or misnested tags: Tags inside
<code>are improperly overlapped, meaning an element opened inside<code>is closed outside it, or vice versa.
This matters for several reasons. Browsers will attempt to "fix" the broken nesting by rearranging the DOM in ways you may not expect, leading to inconsistent rendering and styling. Screen readers and other assistive technologies rely on a well-formed DOM tree, so broken nesting can harm accessibility. Additionally, other validator errors in your document may cascade from this single nesting issue, so fixing it can resolve multiple warnings at once.
When debugging this error, look at other validation errors reported near the same line. Often, a prior error — such as an unclosed tag or an unexpected block element — is the root cause, and the </code> nesting violation is a downstream consequence.
Examples
❌ Block-level element inside <code>
Placing a <div> inside <code> violates the content model. The browser implicitly closes <code> before the <div>, leaving the </code> end tag orphaned.
<p>Example: <code>some text <div>block content</div> more text</code></p>
✅ Use only phrasing content inside <code>
Replace the block-level element with an inline alternative like <span>:
<p>Example: <code>some text <span>inline content</span> more text</code></p>
❌ Overlapping tags with <code>
Here, <em> is opened inside <code> but closed after </code>, creating an overlap:
<p>See <code>myFunction(<em>arg</code>)</em> for details.</p>
✅ Properly nested tags
Ensure every element opened inside <code> is also closed inside it:
<p>See <code>myFunction(<em>arg</em>)</code> for details.</p>
❌ Paragraph inside <code>
A <p> element is not allowed inside <code>:
<code>
<p>First line of code</p>
<p>Second line of code</p>
</code>
✅ Use <pre><code> for multi-line code blocks
For multi-line code, wrap <code> inside a <pre> element and use line breaks instead of paragraphs:
<pre><code>First line of code
Second line of code</code></pre>
❌ List inside <code>
<code>
<ul>
<li>step one</li>
<li>step two</li>
</ul>
</code>
✅ Separate the list from the code markup
Use <code> individually around the inline code portions:
<ul>
<li><code>step one</code></li>
<li><code>step two</code></li>
</ul>
How to Fix
- Find the line referenced by the validator error and look at what's inside the
<code>element. - Check for block-level elements like
<div>,<p>,<ul>,<table>, or headings nested inside<code>. Replace them with phrasing content alternatives or restructure your markup. - Check for overlapping tags where an element started inside
<code>is closed outside it (or the reverse). Make sure every tag opened inside<code>is closed inside it. - Review related errors in the validator output. Often, fixing an earlier nesting or unclosed-tag error will resolve the
</code>violation automatically.
HTML requires elements to be properly nested, meaning you must close child elements before closing their parent elements. When an </em> end tag appears in the wrong place — such as before an inner element has been closed, or after an outer element has already been closed — the browser encounters overlapping tags that violate the HTML specification. This is sometimes called "tag soup."
Browsers attempt to recover from overlapping tags, but the way they do so is unpredictable and may not match your intent. One browser might auto-close the inner element, while another might restructure the DOM differently. This can lead to inconsistent rendering, broken styles, and unexpected behavior across browsers. It also creates problems for assistive technologies like screen readers, which rely on a well-formed document tree to convey meaning to users.
The nesting rule is straightforward: elements must close in the reverse order they were opened. If you open <em> and then open <a>, you must close </a> first, then </em>. Think of it like stacking boxes — you can't remove a box from the bottom without first removing the one on top.
This issue can also arise when <em> is used across block-level boundaries. For example, wrapping <em> around multiple <p> elements or closing </em> inside a <div> when it was opened outside of it will also trigger this error.
Examples
Overlapping with an inline element
The </em> tag closes before the nested <a> tag, creating overlapping elements:
<!-- ❌ Wrong: <em> closes before <a> -->
<p><em><ahref="#">link</em></a></p>
Close the inner <a> element first, then the outer <em>:
<!-- ✅ Correct: tags close in reverse order -->
<p><em><ahref="#">link</a></em></p>
Overlapping with a block-level element
The <em> element spans across a <p> boundary, which is not allowed:
<!-- ❌ Wrong: <em> opened in one <p> and closed in another -->
<p>This is <em>emphasized text</p>
<p>that continues here</em> in a new paragraph.</p>
Apply <em> separately within each <p> element:
<!-- ✅ Correct: <em> is properly contained within each <p> -->
<p>This is <em>emphasized text</em></p>
<p><em>that continues here</em> in a new paragraph.</p>
Multiple levels of nesting
With deeply nested elements, every tag must still close in strict reverse order:
<!-- ❌ Wrong: </em> closes before </strong> -->
<p><em><strong>bold and italic</em></strong></p>
<!-- ✅ Correct: </strong> closes first, then </em> -->
<p><em><strong>bold and italic</strong></em></p>
<em> crossing a <div> boundary
<!-- ❌ Wrong: <em> opened outside <div>, closed inside -->
<em>
<div>
<p>Some content</p>
</em>
</div>
Keep the <em> element entirely inside or outside the block element:
<!-- ✅ Correct: <em> is fully contained within the <div> -->
<div>
<p><em>Some content</em></p>
</div>
How to spot and fix these errors
- Read the error message carefully. The W3C validator usually tells you which line the problem is on and which tags are involved.
- Match every opening tag with its closing tag. Trace through the nesting to make sure each pair is properly contained within its parent.
- Use consistent indentation. Properly indented code makes nesting issues much easier to see at a glance.
- Check for copy-paste errors. Overlapping tags often sneak in when code is moved between elements without updating the surrounding tags.
When the browser encounters a </body> tag while elements like <div>, <section>, <p>, or <span> are still open, it must guess where those elements were supposed to end. Different browsers may guess differently, leading to inconsistent rendering and a DOM structure that doesn't match your intent. This can cause layout problems, broken styling, and accessibility issues — screen readers rely on a well-formed DOM to convey the correct document structure to users.
The root causes of this error typically include:
- Forgetting a closing tag — the most common scenario, especially with deeply nested structures.
- Mismatched tags — closing tags that don't correspond to their opening tags (e.g., opening a
<div>but closing it with</section>). - Copy-paste errors — duplicating or deleting code that leaves behind orphaned opening tags.
- Incorrect nesting — overlapping elements where tags cross boundaries instead of being properly nested.
To fix the issue, work through the validator's error list from top to bottom. The error message usually identifies which elements are unclosed. Find each one and either add the missing closing tag in the correct position or remove the unnecessary opening tag. Using consistent indentation makes it much easier to spot mismatches visually.
Examples
Missing closing tag
This triggers the error because the <section> element is never closed:
<body>
<section>
<h1>Welcome</h1>
<p>Hello, world!</p>
</body>
Add the missing </section> closing tag:
<body>
<section>
<h1>Welcome</h1>
<p>Hello, world!</p>
</section>
</body>
Multiple unclosed elements
Here, both the <div> and the <ul> are left open:
<body>
<divclass="sidebar">
<ul>
<li>Home</li>
<li>About</li>
</body>
Close each element in the correct (reverse) order:
<body>
<divclass="sidebar">
<ul>
<li>Home</li>
<li>About</li>
</ul>
</div>
</body>
Mismatched closing tag
A mismatch between opening and closing tags can also produce this error. The <div> is opened but </section> is used to close it, leaving the <div> unclosed:
<body>
<divclass="content">
<p>Some text here.</p>
</section>
</body>
Fix the closing tag so it matches the opening tag:
<body>
<divclass="content">
<p>Some text here.</p>
</div>
</body>
Overlapping (improperly nested) elements
Elements that overlap instead of nesting properly will also trigger this error. Here the <b> and <i> tags cross each other's boundaries:
<body>
<p><b>Bold and <i>italic</b> text</i></p>
</body>
Ensure elements are closed in the reverse order they were opened:
<body>
<p><b>Bold and <i>italic</i></b><i> text</i></p>
</body>
The HTML specification is clear on this point: an end tag consists solely of </, the tag name, optional whitespace, and >. No attributes, no values, no extra content of any kind is permitted. This rule applies universally to every HTML element.
This error usually occurs due to one of a few common mistakes:
- Misplaced attributes: An attribute like
classoridwas accidentally typed on the closing tag instead of (or in addition to) the opening tag. - Copy-paste errors: When duplicating or restructuring code, attributes may end up attached to the wrong tag.
- Typos or malformed tags: A missing
>on the opening tag can cause the browser or validator to interpret what follows as part of the end tag.
While most browsers are forgiving and will simply ignore attributes on closing tags, this is still a problem. It signals malformed markup that can cause unpredictable behavior in parsers, screen readers, and other tools that process HTML. It also makes your code harder to read and maintain, and it may indicate a deeper structural issue — such as an attribute that was meant to be on the opening tag and is therefore not being applied at all.
Examples
Attribute accidentally placed on the closing tag
This triggers the error because class appears on the </p> end tag:
<p>Welcome to the site.</p class="welcome">
Remove the attribute from the closing tag and place it on the opening tag:
<pclass="welcome">Welcome to the site.</p>
Attribute duplicated on both tags
Sometimes attributes appear on both the opening and closing tags:
<divid="sidebar"class="panel">
<p>Sidebar content</p>
</div id="sidebar">
The fix is to remove all attributes from the closing tag:
<divid="sidebar"class="panel">
<p>Sidebar content</p>
</div>
Missing > on the opening tag causing a cascade
A subtle typo on the opening tag can lead to this error. Here, the missing > after the opening <h2 causes the validator to misinterpret the markup:
<h2class="title"ChapterOne</h2>
Adding the missing > fixes the structure:
<h2class="title">Chapter One</h2>
Multiple elements with the same mistake
This pattern sometimes appears when developers add attributes to closing tags as informal "comments" to track which element is being closed:
<divclass="header">
<navclass="main-nav">
<ul>
<li><ahref="/">Home</a></li>
</ul>
</nav class="main-nav">
</div class="header">
If you want to annotate closing tags for readability, use HTML comments instead:
<divclass="header">
<navclass="main-nav">
<ul>
<li><ahref="/">Home</a></li>
</ul>
</nav><!-- .main-nav -->
</div><!-- .header -->
HTML follows a strict stack-based nesting model. When you open an element, it becomes the "current" element, and any new element you open becomes a child of it. Closing tags must mirror the opening order in reverse. When the validator encounters </i> but the most recently opened unclosed element is something else — like <strong>, <b>, <span>, or <a> — the nesting rules are violated because the <i> element and the other element are overlapping rather than containing one another.
For example, if you write <i>...<strong>...</i>...</strong>, the <i> and <strong> elements overlap. The <i> tries to close while <strong> is still open inside it, and then <strong> tries to close outside of <i>. This creates an ambiguous structure that no valid DOM tree can represent as written. Browsers will attempt to "fix" this using error-recovery algorithms, but the result may differ across browsers and is unlikely to match your intent.
Why this matters
- Unpredictable rendering: Browser error-recovery can silently restructure your markup, leading to missing styles, broken links, or misplaced text.
- Accessibility: Screen readers rely on a well-formed DOM tree. Mis-nested elements can cause assistive technology to misinterpret the structure, reading content out of order or missing emphasis cues entirely.
- Maintainability: Overlapping tags make code harder to read and debug. Future edits are more likely to introduce additional errors.
Common causes
- Overlapping inline elements: Opening
<i>and then<b>(or<strong>,<em>,<span>, etc.) but closing</i>before</b>. - Tags crossing an anchor boundary: Starting
<i>outside an<a>element but closing it inside, or vice versa. - Copy-paste errors: Duplicating a block of HTML that includes icons (e.g., Font Awesome
<i>tags) or screen-reader-only<span>elements, then editing part of it without fixing the tag order. - Mixing presentational and semantic markup: Wrapping
<i>around content that already contains<em>or<strong>, then accidentally closing in the wrong sequence.
How to fix it
- Identify the overlapping pair. The validator message usually points to the line where
</i>appears. Look for the nearest unclosed element that was opened after<i>. - Reorder the closing tags so they mirror the opening order in reverse. If you opened
<i>then<strong>, close</strong>then</i>. - If the overlap is intentional (e.g., you want bold-italic text that transitions to just bold), restructure by closing and reopening elements at the boundary rather than overlapping them.
- Consider semantic alternatives. The
<i>element is for idiomatic text (technical terms, foreign phrases, thoughts). If you need emphasis, use<em>. If you only need italic styling, use CSSfont-style: italic;. Whichever element you choose, the nesting rules are the same.
Examples
Incorrect — overlapping <i> and <strong>
The </i> closes while <strong> is still open inside it:
<p>
<ahref="/about"><i>About <strong>Us</i></strong></a>
</p>
Correct — properly nested
Close <strong> first, then <i>:
<p>
<ahref="/about"><i>About <strong>Us</strong></i></a>
</p>
Incorrect — <i> crossing an anchor boundary
<p>
<i><ahref="/contact">Contact us</i></a> for more info.
</p>
Correct — keep <i> fully inside or fully outside the link
<p>
<ahref="/contact"><i>Contact us</i></a> for more info.
</p>
Incorrect — overlapping inline elements with a style transition
Trying to make "bold italic" transition to "just bold" by overlapping:
<p>
<i>Italic and <b>bold-italic</i> then just bold.</b>
</p>
Correct — close and reopen at the boundary
<p>
<i>Italic and <b>bold-italic</b></i><b> then just bold.</b>
</p>
Incorrect — icon element mis-nested with a span
<button>
<iclass="icon-search"><spanclass="sr-only">Search</i></span>
</button>
Correct — close <span> before <i>
<button>
<iclass="icon-search"><spanclass="sr-only">Search</span></i>
</button>
A quick way to check your nesting is to read through your opening tags in order and then confirm the closing tags appear in exactly the reverse order. If you opened <a>, <i>, <strong>, the closing sequence must be </strong>, </i>, </a>. When in doubt, use your editor's bracket-matching or tag-highlighting feature to visually trace each pair.
When the HTML parser reaches a </li> closing tag, it expects all elements nested within that list item to already be closed. If any child element remains open, the browser must guess where to close it, which can lead to an unexpected DOM structure. This error typically occurs when a closing tag is accidentally omitted, misspelled, or placed in the wrong order.
This matters for several reasons. First, browsers may interpret the intended structure differently, causing inconsistent rendering across platforms. Second, assistive technologies like screen readers rely on a well-formed DOM to convey content correctly — unclosed elements can confuse the reading order or grouping of content. Third, unclosed elements can cascade into other validation errors, making it harder to identify the real problems in your markup.
Common causes of this error include:
- Forgetting to close an inline element like
<span>,<a>,<strong>, or<em>inside a list item. - Forgetting to close a block-level element like
<div>or<p>inside a list item. - Nesting elements in the wrong order, so closing tags don't match their opening tags.
- Typos in closing tags (e.g.,
</sapn>instead of</span>).
To fix the issue, locate the <li> mentioned in the error and check every element opened inside it. Make sure each one has a corresponding, correctly spelled closing tag, and that they are closed in the reverse order they were opened (last opened, first closed).
Examples
Missing closing tag on an inline element
❌ Invalid: The <span> is never closed before </li>.
<ul>
<li>
<span>Example text
</li>
</ul>
✅ Valid: The <span> is properly closed.
<ul>
<li>
<span>Example text</span>
</li>
</ul>
Missing closing tag on a link
❌ Invalid: The <a> element is left open.
<ul>
<li>
<ahref="/about">About us
</li>
<li>
<ahref="/contact">Contact</a>
</li>
</ul>
✅ Valid: Both <a> elements are closed before their parent </li>.
<ul>
<li>
<ahref="/about">About us</a>
</li>
<li>
<ahref="/contact">Contact</a>
</li>
</ul>
Multiple unclosed nested elements
❌ Invalid: Both <strong> and <a> are left open inside the <li>.
<ol>
<li>
<ahref="/sale"><strong>Big sale
</li>
</ol>
✅ Valid: Nested elements are closed in reverse order (innermost first).
<ol>
<li>
<ahref="/sale"><strong>Big sale</strong></a>
</li>
</ol>
Misspelled closing tag
❌ Invalid: The closing tag </sapn> doesn't match <span>, so the <span> remains open.
<ul>
<li>
<span>Item one</sapn>
</li>
</ul>
✅ Valid: The closing tag is spelled correctly.
<ul>
<li>
<span>Item one</span>
</li>
</ul>
Incorrectly ordered closing tags
❌ Invalid: The </em> and </strong> tags are closed in the wrong order, leaving <em> effectively unclosed when </strong> is reached.
<ul>
<li>
<strong><em>Important note</strong></em>
</li>
</ul>
✅ Valid: Closing tags are in the correct reverse order.
<ul>
<li>
<strong><em>Important note</em></strong>
</li>
</ul>
HTML follows a strict nesting model where elements must be properly contained within one another. When you open a <strong> element and then open another element inside it (such as an <a>, <em>, or <span>), you must close the inner element first before closing </strong>. Closing tags out of order creates overlapping elements, which violates the HTML specification.
This matters for several important reasons. First, browsers must guess how to interpret improperly nested markup, and different browsers may resolve the ambiguity differently, leading to inconsistent rendering. Second, assistive technologies like screen readers rely on a well-formed document tree to convey the correct structure and emphasis to users. Overlapping tags produce a broken DOM tree that can confuse these tools. Third, the WHATWG HTML specification explicitly defines the parsing model as a tree structure — elements cannot partially overlap because a tree node can only have one parent.
The fix is straightforward: always close elements in the reverse order they were opened. Think of it as a "last opened, first closed" rule. If <strong> is opened first and <a> is opened second, then </a> must come before </strong>.
Examples
Incorrect: overlapping tags
The </strong> tag closes before the inner <a> tag, violating nesting rules:
<p><strong><ahref="/about">About us</strong></a></p>
Correct: properly nested tags
The <a> element is fully closed before </strong>:
<p><strong><ahref="/about">About us</a></strong></p>
Incorrect: multiple nested elements closed out of order
Here <strong> overlaps with both <em> and <a>:
<p><strong><em><ahref="#">Learn more</strong></em></a></p>
Correct: closing in reverse order
Each tag is closed in the exact reverse order it was opened:
<p><strong><em><ahref="#">Learn more</a></em></strong></p>
Incorrect: strong spanning across block-level boundaries
Sometimes the nesting violation occurs when <strong> wraps across list items or other structures:
<ul>
<li><strong>First item</li>
<li>Second item</strong></li>
</ul>
Correct: apply strong within each element individually
Keep <strong> fully contained within each <li>:
<ul>
<li><strong>First item</strong></li>
<li><strong>Second item</strong></li>
</ul>
A helpful way to spot these issues is to visually trace your opening and closing tags — if you draw lines connecting each pair, the lines should never cross. If they do, you have a nesting violation that needs to be reordered.
HTML follows strict nesting rules: elements must be closed in the reverse order they were opened, like a stack. When the validator encounters </X> but the current open element is Y, it means something has gone wrong in the document structure. The browser's parser will attempt to recover from this mismatch, but the result may not reflect your intended layout or semantics.
There are several common causes for this error:
- Typos in tag names — for example, opening a
<div>but closing it with</dvi>. - Mismatched tags — opening one element but closing a different one, such as
<strong>closed with</em>. - Incorrect nesting order — overlapping elements where tags cross boundaries instead of being properly nested.
- Missing closing tags — a forgotten closing tag causes subsequent closing tags to be misaligned with the parser's expectation.
This matters because improperly nested HTML can cause unpredictable rendering across browsers, break CSS styling, interfere with JavaScript DOM manipulation, and create accessibility problems for screen readers that rely on a well-formed document tree.
To fix this error, trace back from the reported line to find where the mismatch originates. Ensure that every opening tag has a corresponding closing tag with the exact same name, and that elements are closed in the correct order (last opened, first closed).
Examples
Typo in the closing tag
<!-- ❌ Wrong: closing tag is misspelled -->
<section>
<p>Hello world</p>
</secton>
<!-- ✅ Fixed: closing tag matches the opening tag -->
<section>
<p>Hello world</p>
</section>
Mismatched tags
<!-- ❌ Wrong: <strong> is closed with </em> -->
<p>This is <strong>important</em> text.</p>
<!-- ✅ Fixed: closing tag matches the opening tag -->
<p>This is <strong>important</strong> text.</p>
Incorrectly nested (overlapping) elements
<!-- ❌ Wrong: <b> and <i> overlap each other -->
<p><b>Bold <i>and italic</b> text</i></p>
The validator sees </b> when the current open element is <i>. Elements must nest completely inside one another without overlapping.
<!-- ✅ Fixed: elements are properly nested -->
<p><b>Bold <i>and italic</i></b><i> text</i></p>
Missing closing tag causing a cascade of errors
<!-- ❌ Wrong: missing </div> for the inner div -->
<divclass="outer">
<divclass="inner">
<p>Content</p>
</div>
Here the single </div> closes inner, leaving outer unclosed. If more HTML follows, subsequent closing tags will be misaligned, potentially producing this error further down in the document.
<!-- ✅ Fixed: both divs are properly closed -->
<divclass="outer">
<divclass="inner">
<p>Content</p>
</div>
</div>
Tips for debugging
- Work from the first error — in HTML validation, one early mistake can cascade into many subsequent errors. Fix the first reported mismatch and re-validate before tackling later errors.
- Use consistent indentation — properly indented code makes it much easier to spot where nesting goes wrong.
- Use an editor with bracket/tag matching — most code editors can highlight matching opening and closing tags, making mismatches immediately visible.
The HTML specification defines strict rules about how elements must be nested and closed. When the parser reaches a point where it needs to close a parent element — either because it encounters the parent's explicit end tag or because a sibling element implicitly closes it — any child elements that are still open create a problem. The browser's error-recovery mechanism will attempt to auto-close those elements, but the result may not match your intent, leading to unexpected DOM structures, broken layouts, or accessibility issues.
This error is especially common with <li>, <p>, and <td> elements because their end tags are optional in HTML. When the parser sees a new <li> while a previous <li> is still open, it implicitly closes the first one. If there are unclosed child elements inside that first <li>, you'll get this warning.
There are several common scenarios that trigger this issue:
- Forgetting to close an inline element (like
<span>,<a>, or<strong>) inside a list item, table cell, or paragraph. - Mismatched or misordered closing tags where elements overlap instead of nesting properly.
- Accidentally omitting a closing tag in a deeply nested structure.
Beyond validation, unclosed elements can cause real problems. Screen readers may misinterpret the document structure, CSS selectors may not match as expected, and browsers may construct a DOM tree that differs from what you intended. Fixing these issues ensures consistent rendering across browsers and a reliable experience for all users.
How to Fix
- Locate the open element mentioned in the error. The validator usually tells you which element is still open.
- Add the missing closing tag in the correct position — before the parent element's end tag or before the next sibling that would implicitly close the parent.
- Verify proper nesting order. Elements must be closed in the reverse order they were opened (last opened, first closed).
Examples
Unclosed <span> inside a list item
The <span> is never closed, so when the parser encounters the second <li>, it implicitly closes the first <li> while <span> is still open:
<!-- ❌ Triggers: End tag "li" implied, but there were open elements -->
<ul>
<li><span>First item</li>
<li>Second item</li>
</ul>
Close the <span> before the <li> ends:
<!-- ✅ Correct -->
<ul>
<li><span>First item</span></li>
<li>Second item</li>
</ul>
Implicit <li> closure with open child element
When a new <li> appears, the previous <li> is auto-closed. If a <strong> tag is still open inside it, this error is raised:
<!-- ❌ Triggers the error -->
<ul>
<li><strong>Important text<li>Other item</li>
</ul>
<!-- ✅ Correct -->
<ul>
<li><strong>Important text</strong></li>
<li>Other item</li>
</ul>
Unclosed <a> inside a <p> before a block element
When the parser encounters a <div> (or another block element), it implicitly closes the <p>, leaving the <a> unclosed:
<!-- ❌ Triggers the error -->
<p>Visit <ahref="/about">our page
<div>Other content</div>
<!-- ✅ Correct -->
<p>Visit <ahref="/about">our page</a></p>
<div>Other content</div>
Misordered closing tags
Closing tags must appear in reverse order of their opening tags. Overlapping elements are invalid:
<!-- ❌ Triggers the error -->
<p><em><strong>Bold italic text</em></strong></p>
<!-- ✅ Correct: close in reverse order -->
<p><em><strong>Bold italic text</strong></em></p>
Unclosed element in a table cell
<!-- ❌ Triggers the error -->
<table>
<tr>
<td><span>Data</td>
<td>More data</td>
</tr>
</table>
<!-- ✅ Correct -->
<table>
<tr>
<td><span>Data</span></td>
<td>More data</td>
</tr>
</table>
When you see this error, look for the unclosed element the validator identifies, add its closing tag in the correct position, and make sure all elements follow proper nesting order. If multiple instances of this error appear in your document, fixing the first one often resolves several others downstream.
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