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 <a> element has an implicit ARIA role of link (when it has an href) or generic (when it doesn't). Certain ARIA state attributes, like aria-checked, are only valid on elements with specific roles that support them. For instance, aria-checked is designed for roles like checkbox, menuitemcheckbox, radio, switch, or option. If you place aria-checked on an <a> element without assigning one of these compatible roles, the validator raises this error because the attribute doesn't make sense in the context of the element's current role.
This matters for several reasons. Screen readers and other assistive technologies rely on the relationship between roles and their supported states to convey meaningful information to users. An aria-checked attribute on a plain link creates a confusing experience — the user hears that something is "checked" but the element is announced as a link, which isn't a concept that supports checked/unchecked states. This mismatch can make interfaces unusable for people relying on assistive technology.
To fix this issue, you need to either:
- Add an appropriate
rolethat supports the ARIA state attribute you're using. - Use a more semantically appropriate element, such as
<input type="checkbox">or<button>, which natively supports the concept of being checked or toggled. - Remove the unsupported ARIA attribute if it doesn't actually reflect the element's behavior.
Examples
Incorrect: aria-checked without a compatible role
This triggers the validation error because <a> doesn't support aria-checked without an explicit role:
<ahref="#"aria-checked="true">Dark mode</a>
Fixed: Adding a compatible role
Adding role="menuitemcheckbox" (within a menu context) or role="switch" makes aria-checked valid:
<ulrole="menu">
<lirole="none">
<ahref="#"role="menuitemcheckbox"aria-checked="true">Show notifications</a>
</li>
<lirole="none">
<ahref="#"role="menuitemcheckbox"aria-checked="false">Dark mode</a>
</li>
</ul>
Fixed: Using a <button> with role="switch" instead
In many cases, a <button> is a better semantic fit than an <a> for toggle-like interactions:
<buttonrole="switch"aria-checked="true">Dark mode</button>
Correct: Tab list using <a> elements with proper roles
When building a tab interface with anchor elements, each tab needs role="tab" along with supporting attributes like aria-selected:
<divclass="tab-interface">
<divrole="tablist"aria-label="Settings">
<arole="tab"href="#panel-1"aria-selected="true"aria-controls="panel-1"id="tab-1">
General
</a>
<arole="tab"href="#panel-2"aria-selected="false"aria-controls="panel-2"id="tab-2"tabindex="-1">
Advanced
</a>
</div>
<divid="panel-1"role="tabpanel"tabindex="0"aria-labelledby="tab-1">
<p>General settings content</p>
</div>
<divid="panel-2"role="tabpanel"tabindex="0"aria-labelledby="tab-2"hidden>
<p>Advanced settings content</p>
</div>
</div>
Incorrect: aria-selected on a plain <a> without a role
<ahref="/settings"aria-selected="true">Settings</a>
Fixed: Adding the appropriate role
<ahref="/settings"role="tab"aria-selected="true">Settings</a>
When choosing a fix, always consider whether the <a> element is truly the best choice. If the element doesn't navigate the user to a new URL, a <button> is usually more appropriate. Reserve <a> for actual navigation, and use ARIA roles and states only when they accurately describe the element's behavior in the interface.
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 implicitrole="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
hrefis 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
hrefattribute if the element should be a link. This is the most common and recommended fix. - Add a
roleattribute if you're deliberately using<a>withouthreffor a specific purpose, such asrole="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
<aonclick="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
<ahref="/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
<arole="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
<buttontype="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)
<arole="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 defines <button> as a versatile interactive element, but its behavior changes depending on context. When a <button> is placed inside a <form> without a type attribute, it defaults to type="submit", which can cause unexpected form submissions. The validator flags this because relying on the implicit default is ambiguous and error-prone. Explicitly setting the type attribute makes the button's intent clear to both developers and browsers.
The three valid values for the type attribute are:
submit— submits the parent form's data to the server.reset— resets all form controls to their initial values.button— performs no default action; behavior is defined via JavaScript.
When a <button> is given an ARIA role of checkbox, switch, or menuitemcheckbox, the validator expects an aria-checked attribute to accompany it. These roles describe toggle controls that have a checked or unchecked state, so assistive technologies need to know the current state. Without aria-checked, screen readers cannot communicate whether the control is on or off, making the interface inaccessible.
The aria-checked attribute accepts the following values:
true— the control is checked or on.false— the control is unchecked or off.mixed— the control is in an indeterminate state (valid forcheckboxandmenuitemcheckboxroles only).
How to fix it
For standard buttons, add the type attribute with the appropriate value. If the button triggers JavaScript behavior and is not meant to submit a form, use type="button". If it submits a form, use type="submit" explicitly to make the intent clear.
For toggle buttons, ensure the <button> has both a role attribute (such as checkbox or switch) and an aria-checked attribute that reflects the current state. You should also include type="button" to prevent unintended form submission. Use JavaScript to toggle the aria-checked value when the user interacts with the button.
Examples
Missing type attribute
This triggers the validator warning because the type is not specified:
<formaction="/search">
<inputtype="text"name="q">
<button>Search</button>
</form>
Fixed by adding an explicit type:
<formaction="/search">
<inputtype="text"name="q">
<buttontype="submit">Search</button>
</form>
Button used outside a form without type
<buttononclick="openMenu()">Menu</button>
Fixed by specifying type="button":
<buttontype="button"onclick="openMenu()">Menu</button>
Toggle button missing aria-checked
A button with role="switch" but no aria-checked attribute:
<buttontype="button"role="switch">Dark Mode</button>
Fixed by adding aria-checked:
<buttontype="button"role="switch"aria-checked="false">Dark Mode</button>
Checkbox-style toggle button
A button acting as a checkbox must include both role="checkbox" and aria-checked:
<buttontype="button"role="checkbox"aria-checked="false">
Enable notifications
</button>
Complete toggle example with all required attributes
<buttontype="button"role="switch"aria-checked="false"id="wifi-toggle">
Wi-Fi
</button>
<script>
document.getElementById("wifi-toggle").addEventListener("click",function(){
constisChecked=this.getAttribute("aria-checked")==="true";
this.setAttribute("aria-checked",String(!isChecked));
});
</script>
In this example, the type="button" prevents form submission, the role="switch" tells assistive technologies this is a toggle, and aria-checked is updated dynamically to reflect the current state. This ensures the button is fully accessible and passes validation.
The <dl> element represents a description list — a collection of terms (<dt>) paired with their descriptions (<dd>). According to the HTML specification, the permitted content of a <dl> is either groups of <dt> and <dd> elements directly, or <div> elements that wrap those groups. This <div> wrapping option exists specifically to help with styling, since it lets you apply CSS to a term-description pair as a unit.
However, these wrapper <div> elements are not general-purpose containers inside <dl>. The spec requires that each <div> inside a <dl> contains at least one <dt> or <dd> child. A <div> that is empty, or that contains other elements like <span>, <p>, or another <div>, violates this rule and produces a validation error.
This matters for several reasons. Screen readers and assistive technologies rely on the semantic structure of description lists to convey the relationship between terms and definitions. An empty or improperly structured <div> inside a <dl> breaks that semantic contract, potentially confusing both assistive technology and browsers. Keeping your markup valid also ensures consistent rendering across all browsers.
How to Fix It
- Ensure every
<div>inside a<dl>contains at least one<dt>or<dd>— don't leave wrapper<div>elements empty. - Don't put non-
<dt>/<dd>content directly inside these<div>wrappers — elements like<span>,<p>, or nested<div>elements should be placed inside a<dt>or<dd>, not as siblings to them. - Remove unnecessary
<div>wrappers — if a<div>inside a<dl>serves no styling or grouping purpose, remove it entirely.
Examples
❌ Empty <div> inside a <dl>
<dl>
<div>
</div>
<div>
<dt>HTML</dt>
<dd>A markup language for structuring web content.</dd>
</div>
</dl>
The first <div> is empty and has no <dt> or <dd> child, triggering the error.
❌ <div> with only non-<dt>/<dd> children
<dl>
<div>
<p>This is a description list:</p>
</div>
<div>
<dt>CSS</dt>
<dd>A style sheet language for describing presentation.</dd>
</div>
</dl>
The first <div> contains a <p> element but no <dt> or <dd>, which is invalid.
✅ Each <div> contains <dt> and <dd> elements
<dl>
<div>
<dt>HTML</dt>
<dd>A markup language for structuring web content.</dd>
</div>
<div>
<dt>CSS</dt>
<dd>A style sheet language for describing presentation.</dd>
</div>
</dl>
✅ Using <dl> without <div> wrappers
If you don't need <div> elements for styling, you can place <dt> and <dd> directly inside the <dl>:
<dl>
<dt>HTML</dt>
<dd>A markup language for structuring web content.</dd>
<dt>CSS</dt>
<dd>A style sheet language for describing presentation.</dd>
</dl>
✅ A <dt> with multiple <dd> descriptions in a <div>
A single term can have multiple descriptions. The <div> is valid as long as it contains at least one <dt> or <dd>:
<dl>
<div>
<dt>JavaScript</dt>
<dd>A programming language for the web.</dd>
<dd>Supports event-driven and functional programming.</dd>
</div>
</dl>
The aria-expanded attribute cannot be used on a plain div element without also specifying a role attribute.
The aria-expanded attribute indicates whether a grouping of content that the element owns or controls is currently expanded or collapsed. However, this attribute is only valid on elements that have an appropriate implicit or explicit role. A plain div has no implicit ARIA role, so you must assign one explicitly.
The aria-expanded attribute is commonly used with interactive roles such as button, combobox, treeitem, or link. Adding the correct role tells assistive technologies what kind of element the user is interacting with, making aria-expanded meaningful in context.
HTML Examples
❌ Invalid: aria-expanded on a plain div
<divaria-expanded="false">
Menu content
</div>
✅ Valid: aria-expanded with an appropriate role
<divrole="button"aria-expanded="false">
Menu content
</div>
Alternatively, consider using a native HTML element that already carries the correct semantics, which avoids the need for a role attribute entirely:
<buttonaria-expanded="false">
Toggle Menu
</button>
Using a native <button> is generally preferred over <div role="button"> because it comes with built-in keyboard interaction and focus behavior.
The aria-required attribute tells assistive technologies that a form field must be filled in before the form can be submitted. However, this attribute is only valid on elements that function as interactive widgets. A bare div has no implicit ARIA role, so assistive technologies have no context for what kind of input is expected. The validator flags this because an aria-required attribute on a generic div is effectively meaningless without additional ARIA attributes that define the element's role and behavior.
This matters for several reasons:
- Accessibility: Screen readers and other assistive technologies rely on roles to understand how to present an element to users. Without a role,
aria-requiredprovides incomplete information. - Standards compliance: The WAI-ARIA specification defines which attributes are allowed on which roles. Using
aria-requiredwithout an established role violates these constraints. - Browser behavior: Browsers may ignore or misinterpret ARIA attributes when they appear on elements that lack the proper role context.
How to fix it
Option 1: Use native semantic HTML (preferred)
Whenever possible, use native HTML form elements. They come with built-in accessibility semantics, keyboard interaction, and validation — no ARIA needed.
Replace a div with aria-required="true" with an appropriate form control using the native required attribute:
<inputtype="text"required>
<selectrequired>
<optionvalue="">Choose one</option>
<optionvalue="1">Option 1</option>
</select>
<textarearequired></textarea>
Option 2: Add an appropriate role attribute
When you must use a div as a custom widget (styled and enhanced with CSS and JavaScript), add the correct role attribute to give it semantic meaning. Choose the role that matches the widget's actual behavior — don't just pick one arbitrarily.
Common roles that support aria-required:
combobox— a custom dropdown with text inputlistbox— a custom selection listradiogroup— a group of radio-like optionsspinbutton— a numeric stepper (also requiresaria-valuemax,aria-valuemin, andaria-valuenow)textbox— a custom text input
Option 3: Add other qualifying ARIA attributes
For certain widgets like sliders or spinbuttons, you may need aria-valuemax, aria-valuemin, and aria-valuenow in addition to (or as part of) defining the role. These attributes inherently establish a widget context.
Examples
❌ Invalid: aria-required on a plain div
<divaria-required="true">
<divdata-value="One">1</div>
<divdata-value="Two">2</div>
<divdata-value="Three">3</div>
</div>
This triggers the validation error because the div has no role or other ARIA attributes to define what kind of widget it is.
✅ Fixed: Adding the appropriate role
<divaria-required="true"role="radiogroup"aria-label="Pick a number">
<divrole="radio"aria-checked="false"tabindex="0">1</div>
<divrole="radio"aria-checked="false"tabindex="0">2</div>
<divrole="radio"aria-checked="false"tabindex="0">3</div>
</div>
Adding role="radiogroup" gives the div a semantic identity. Note that child elements also need appropriate roles and attributes for the widget to be fully accessible.
✅ Fixed: Using native HTML instead
<fieldset>
<legend>Pick a number</legend>
<label><inputtype="radio"name="number"value="1"required> 1</label>
<label><inputtype="radio"name="number"value="2"> 2</label>
<label><inputtype="radio"name="number"value="3"> 3</label>
</fieldset>
This approach uses native radio buttons with the required attribute, eliminating the need for ARIA entirely. The browser handles accessibility, keyboard navigation, and form validation automatically.
✅ Fixed: A custom spinbutton with value attributes
<divrole="spinbutton"
aria-required="true"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="50"
aria-label="Quantity"
tabindex="0">
50
</div>
For a spinbutton role, you must also provide aria-valuemin, aria-valuemax, and aria-valuenow to fully describe the widget's state.
The HTML specification defines a strict content model for the <ul> (unordered list) element: its permitted content is zero or more <li> elements, optionally intermixed with <script> and <template> elements. A <div> is not among these allowed children, so placing one directly inside a <ul> produces a validation error.
This issue commonly arises when developers try to group or wrap list items for styling or layout purposes. For example, you might want to add a container around certain <li> elements for flexbox or grid alignment, or you might be using a templating system that injects wrapper <div> elements into your list markup.
Why this matters
- Browser parsing inconsistencies: When browsers encounter invalid nesting, they attempt to fix the DOM structure automatically, but different browsers may handle it differently. This can lead to unexpected rendering where list items appear outside their intended container or styles break unpredictably.
- Accessibility: Screen readers and assistive technologies rely on correct semantic structure to convey list relationships to users. A
<div>breaking the<ul>→<li>relationship can cause assistive tools to misinterpret or skip list content entirely. - Standards compliance: Invalid HTML can cause cascading parser errors — note that the validator message says "Suppressing further errors from this subtree," meaning additional issues within that structure may be hidden from you.
How to fix it
- Move the
<div>inside the<li>: If you need a wrapper for styling, place it inside the list item rather than around it. - Remove the
<div>entirely: If it serves no purpose, simply remove it and let the<li>elements sit directly inside the<ul>. - Use CSS on the
<li>elements: In many cases, you can apply the styles you need directly to the<li>elements without an extra wrapper. - Use
role="list"on a<div>parent: If your layout truly requires<div>wrappers, consider restructuring with ARIA roles, though native semantic elements are always preferred.
Examples
❌ Incorrect: <div> as a direct child of <ul>
<ul>
<div>
<li>Apples</li>
<li>Bananas</li>
</div>
<div>
<li>Carrots</li>
<li>Dates</li>
</div>
</ul>
✅ Correct: Move the <div> inside each <li>
<ul>
<li><div>Apples</div></li>
<li><div>Bananas</div></li>
<li><div>Carrots</div></li>
<li><div>Dates</div></li>
</ul>
✅ Correct: Remove the <div> entirely
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Carrots</li>
<li>Dates</li>
</ul>
❌ Incorrect: Using a <div> as a styling wrapper around list items
<ulclass="product-list">
<divclass="row">
<li>Product A</li>
<li>Product B</li>
<li>Product C</li>
</div>
</ul>
✅ Correct: Apply layout styles directly to the <ul> and <li> elements
<ulclass="product-list row">
<li>Product A</li>
<li>Product B</li>
<li>Product C</li>
</ul>
❌ Incorrect: Template or component wrapper inserting a <div>
This often happens in frameworks where a component renders a wrapping <div>:
<ul>
<divclass="list-item-wrapper">
<li>Item 1</li>
</div>
<divclass="list-item-wrapper">
<li>Item 2</li>
</div>
</ul>
✅ Correct: Move the wrapper class to the <li> itself
<ul>
<liclass="list-item-wrapper">Item 1</li>
<liclass="list-item-wrapper">Item 2</li>
</ul>
The same rule applies to <ol> (ordered list) elements — they share the same content model restriction. Always ensure that <li> is the direct child of <ul> or <ol>, and place any additional wrapper elements inside the <li> rather than around it.
According to the HTML specification, the <dl> element's content model requires one or more groups, where each group consists of one or more <dt> elements followed by one or more <dd> elements. Alternatively, these groups can be wrapped in <div> elements for styling purposes. An empty <dl> or one missing either side of the pairing is invalid.
This matters for several reasons. Screen readers and other assistive technologies rely on the proper structure of description lists to convey the relationship between terms and their descriptions. When the structure is incomplete, these tools cannot communicate the intended meaning to users. Browsers may also render malformed description lists inconsistently, leading to unpredictable layouts.
To fix this issue, make sure every <dl> contains at least one <dt> element paired with at least one <dd> element. If you only have a <dd>, add the corresponding <dt>. If you only have a <dt>, add the corresponding <dd>. If the list is empty or you don't have content for both parts, consider whether a <dl> is the right element for your use case—a <ul> or <p> might be more appropriate.
Examples
Missing <dt> element
A <dd> without its corresponding <dt> triggers this error:
<dl>
<dd>A box without hinges, key, or lid, yet golden treasure inside is hid.</dd>
</dl>
Add the missing <dt> to complete the group:
<dl>
<dt>Egg</dt>
<dd>A box without hinges, key, or lid, yet golden treasure inside is hid.</dd>
</dl>
Missing <dd> element
A <dt> without a following <dd> also triggers this error:
<dl>
<dt>Egg</dt>
</dl>
Add the missing <dd> to provide the description:
<dl>
<dt>Egg</dt>
<dd>A box without hinges, key, or lid, yet golden treasure inside is hid.</dd>
</dl>
Empty <dl> element
An empty description list is invalid:
<dl></dl>
Either populate it with a complete term-description group or remove the element entirely.
Multiple terms and descriptions
A single <dt> can have multiple <dd> elements, and multiple <dt> elements can share the same <dd>. Both are valid:
<dl>
<dt>Egg</dt>
<dt>Ova</dt>
<dd>A box without hinges, key, or lid, yet golden treasure inside is hid.</dd>
<dd>An oval body laid by birds, reptiles, and other creatures.</dd>
</dl>
Using <div> wrappers
You can wrap each name-value group in a <div> for styling purposes, but each <div> must still contain the required <dt> and <dd> pairing:
<dl>
<div>
<dt>Egg</dt>
<dd>A box without hinges, key, or lid, yet golden treasure inside is hid.</dd>
</div>
<div>
<dt>Sun</dt>
<dd>An eye in a blue face saw an eye in a green face.</dd>
</div>
</dl>
The <dl> element represents a description list — a collection of name-value groups where <dt> elements provide the terms (or names) and <dd> elements provide the associated descriptions (or values). According to the HTML specification, the content model for <dl> requires that each <dt> group be followed by one or more <dd> elements. A <dl> with only <dt> elements and no <dd> elements is invalid HTML.
This matters for several reasons. Assistive technologies like screen readers rely on the proper <dt>/<dd> pairing to convey the relationship between terms and their descriptions. A description list without descriptions is semantically meaningless — it's like having a dictionary with words but no definitions. Browsers may also render the list inconsistently when the expected structure is incomplete.
Common causes of this error include:
- Accidentally using
<dl>and<dt>to create a simple list instead of using<ul>or<ol>. - Dynamically generated content where the
<dd>elements are missing due to empty data. - Incomplete markup where the developer forgot to add the description elements.
To fix this issue, make sure every <dt> element inside a <dl> is followed by at least one <dd> element. If you don't actually need a description list, consider using a different element like <ul> for simple lists.
Examples
Invalid: <dl> with only <dt> and no <dd>
<dl>
<dt>The Meaning of Life</dt>
</dl>
This is invalid because the <dt> term has no corresponding <dd> description.
Fixed: Adding a <dd> element
<dl>
<dt>The Meaning of Life</dt>
<dd>A philosophical question about the significance of existence.</dd>
</dl>
Invalid: Multiple terms without any descriptions
<dl>
<dt>HTML</dt>
<dt>CSS</dt>
<dt>JavaScript</dt>
</dl>
This triggers the error because none of the terms have associated descriptions.
Fixed: Each term with a description
<dl>
<dt>HTML</dt>
<dd>A markup language for structuring web content.</dd>
<dt>CSS</dt>
<dd>A style sheet language for describing presentation.</dd>
<dt>JavaScript</dt>
<dd>A programming language for web interactivity.</dd>
</dl>
Valid: Multiple terms sharing a single description
Multiple <dt> elements can share a single <dd>, which is useful for synonyms or aliases:
<dl>
<dt>Laptop</dt>
<dt>Notebook</dt>
<dd>A portable personal computer with a clamshell form factor.</dd>
</dl>
Valid: A single term with multiple descriptions
A <dt> can also be followed by multiple <dd> elements:
<dl>
<dt>Python</dt>
<dd>A large constricting snake.</dd>
<dd>A high-level programming language.</dd>
</dl>
Valid: Using <div> to wrap name-value groups
The HTML spec allows wrapping each <dt>/<dd> group in a <div> for styling purposes. Each <div> must still contain at least one <dd>:
<dl>
<div>
<dt>Name</dt>
<dd>Jane Doe</dd>
</div>
<div>
<dt>Email</dt>
<dd>jane@example.com</dd>
</div>
</dl>
Alternative: Use <ul> when descriptions aren't needed
If you only need a list of items without descriptions, a <dl> is the wrong element. Use an unordered list instead:
<ul>
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
The <title> element is a required child of <head> according to the HTML specification. It defines the document's title, which appears in the browser tab, is used as the default name when bookmarking the page, and is displayed as the clickable heading in search engine results. Omitting it violates the HTML standard and triggers this validation error.
Beyond standards compliance, the <title> element is critical for accessibility. Screen readers announce the page title when a user navigates to a new page or switches between tabs, giving them immediate context about where they are. A missing title forces assistive technology users to explore the page content to understand its purpose. Search engines also rely heavily on the <title> for indexing and ranking, so omitting it can hurt discoverability.
There are several common causes for this error:
- The
<title>element is simply missing. This often happens in boilerplate code or quick prototypes where it's overlooked. - Duplicate
<head>sections. If your HTML contains two<head>elements (for example, from a copy-paste error or a templating mistake), the validator may flag the second one as missing a<title>. - The
<title>is placed outside<head>. If the<title>element accidentally ends up in the<body>or before the<head>, the validator won't count it as a child of<head>. - The
<title>is empty. While an empty<title>element (<title></title>) may not trigger this specific error, some validators will flag it separately. Always include descriptive text.
Examples
Missing <title> element
This triggers the validation error because the <head> has no <title>:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
<title> placed outside <head>
Here the <title> exists but is in the wrong location, so the <head> is still considered to be missing it:
<!DOCTYPE html>
<htmllang="en">
<title>My Page</title>
<head>
<metacharset="utf-8">
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Duplicate <head> sections
A templating error or copy-paste mistake can introduce a second <head>, which lacks its own <title>:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<head>
<metaname="viewport"content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Correct usage
Place a single <title> element with descriptive text inside the <head>:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<metaname="viewport"content="width=device-width, initial-scale=1">
<title>Welcome — My Website</title>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Tips for a good <title>
- Keep it concise but descriptive — aim for roughly 50–60 characters.
- Make it unique for each page on your site. Avoid generic titles like "Untitled" or "Page".
- Front-load the most important information. For example,
Contact Us — My Companyis more useful thanMy Company — Contact Uswhen users scan many browser tabs. - Avoid duplicating the
<h1>verbatim; the title should provide broader context (such as including the site name), while the<h1>focuses on page-specific content.
Every <img> element must include at least a src or a srcset attribute to be valid HTML.
The <img> element exists to embed an image into the document, and it needs to know where that image is. The src attribute provides a single URL for the image, while srcset lets you offer multiple image sources for different screen sizes or resolutions.
You might run into this error when using JavaScript to set the image source dynamically, or when using lazy-loading libraries that store the URL in a data- attribute like data-src. While those techniques work at runtime, they produce invalid HTML because the validator still expects src or srcset to be present in the markup.
If you genuinely don't have a source yet, you can use a placeholder or a transparent pixel as the src value.
Invalid Example
<imgalt="A cute cat"loading="lazy"data-src="cat.jpg">
Valid Examples
Using src:
<imgsrc="cat.jpg"alt="A cute cat">
Using srcset:
<imgsrcset="cat-400.jpg 400w, cat-800.jpg 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A cute cat">
Using both src and a lazy-loading data-src (keeps the markup valid while still supporting lazy loading):
<imgsrc="placeholder.jpg"data-src="cat.jpg"alt="A cute cat">
The HTML specification mandates src on the <img> element because an image without a source has nothing to render. Omitting it produces invalid markup and unpredictable browser behavior — some browsers display a broken image icon, while others render nothing at all.
This issue commonly occurs in a few scenarios:
- Templating placeholders — A developer adds an
<img>tag intending to populate thesrcdynamically but forgets to set a default value. - Lazy loading implementations — Some lazy-loading scripts store the real URL in a
data-srcattribute and omitsrcentirely, which results in invalid HTML. - Incomplete markup — The attribute is simply forgotten during development.
How to fix it
- Add a
srcattribute with a valid URL pointing to your image. - If you're using lazy loading and want to defer the actual image source, set
srcto a lightweight placeholder (such as a tiny transparent image or a low-quality preview) and use the nativeloading="lazy"attribute instead of removingsrc.
Examples
❌ Missing src attribute
<imgalt="A sunset over the ocean">
This triggers the validation error because src is absent.
❌ Source stored only in a data- attribute
<imgdata-src="/images/photo.jpg"alt="A sunset over the ocean">
While data-src is a valid custom data attribute, it does not satisfy the requirement for src. The validator will still report the error.
✅ Correct usage with src
<imgsrc="/images/photo.jpg"alt="A sunset over the ocean">
✅ Lazy loading with a placeholder src
<img
src="/images/photo-placeholder.jpg"
data-src="/images/photo-full.jpg"
alt="A sunset over the ocean"
loading="lazy">
Here, src points to a small placeholder image so the markup stays valid, while data-src holds the full-resolution URL for your lazy-loading script to swap in.
✅ Native lazy loading (no JavaScript needed)
<imgsrc="/images/photo.jpg"alt="A sunset over the ocean"loading="lazy">
Modern browsers support the loading="lazy" attribute natively, so you can keep a valid src and still defer off-screen images without any custom scripting.
The <link> element defines a relationship between the current document and an external resource. It's most often used in the <head> to load stylesheets, declare icons, specify canonical URLs, or provide metadata. The HTML specification requires that at least one of href, itemprop, property, rel, or resource be present on any <link> element. A bare <link> tag with none of these attributes has no defined purpose and is therefore invalid.
This validation error typically occurs in a few scenarios:
- A
<link>element was added as a placeholder and never completed. - Attributes were accidentally removed or misspelled during editing.
- A templating engine or build tool generated an incomplete
<link>tag. - The
relattribute was omitted when onlyhrefwas technically sufficient, but the developer intended to specify a relationship.
While browsers may silently ignore an empty <link> element, leaving it in your markup creates clutter, signals incomplete code, and violates the HTML standard. Keeping your HTML valid ensures predictable behavior across browsers and assistive technologies.
How to fix it
Check every <link> element in your document and make sure it includes at least one of the required attributes. In practice, most <link> elements should have both rel and href:
rel— specifies the relationship (e.g.,stylesheet,icon,canonical,preconnect).href— provides the URL of the linked resource.itemprop— used for microdata markup.property— used for RDFa or Open Graph metadata.resource— used in RDFa to identify a resource by URI.
If a <link> element has no valid purpose, remove it entirely.
Examples
Invalid: <link> with no required attributes
<linktype="text/css">
The type attribute alone does not satisfy the requirement. The validator will flag this element.
Invalid: <link> with only crossorigin
<linkcrossorigin="anonymous">
crossorigin is not one of the required attributes, so this is still invalid.
Valid: stylesheet with rel and href
<linkrel="stylesheet"href="styles.css">
Valid: favicon with rel and href
<linkrel="icon"href="/favicon.ico"type="image/x-icon">
Valid: preconnect hint
<linkrel="preconnect"href="https://fonts.googleapis.com">
Valid: canonical URL
<linkrel="canonical"href="https://example.com/page">
Valid: Open Graph metadata with property
<linkproperty="og:image"href="https://example.com/image.png">
Valid: microdata with itemprop
<linkitemprop="url"href="https://example.com">
Full document example
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
<linkrel="stylesheet"href="styles.css">
<linkrel="icon"href="/favicon.ico">
<linkrel="canonical"href="https://example.com/my-page">
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
Each <link> element in this document has both a rel and an href attribute, making them valid and clearly communicating their purpose to browsers and validators.
A <link> element must have either an href or a resource attribute. Without one of these, the browser has no way to know what the element points to.
The <link> element defines a relationship between the current document and an external resource. The href attribute specifies the URL of that resource. When href is missing, the element is incomplete and has no effect.
A common cause is forgetting to add the href when writing a <link> for a stylesheet, icon, or preload directive. Another cause is accidentally leaving a <link> tag in the markup after removing its URL during editing.
The resource attribute is far less common and appears in RDFa contexts. For most HTML documents, href is the attribute you want.
Invalid example
<linkrel="stylesheet">
The rel attribute is present, but there is no href telling the browser where to find the stylesheet.
Valid example
<linkrel="stylesheet"href="styles.css">
Adding the href attribute resolves the validation error.
The <link> element connects your HTML document to external resources like stylesheets, icons, fonts, and prefetched pages. According to the HTML specification, a <link> element must include at least one of rel, itemprop, or property so that its purpose is clearly defined. A bare <link> with only an href is meaningless—it points to a resource but doesn't explain what that resource is for. The validator raises this error to enforce that every <link> carries semantic meaning.
This matters for several reasons. Browsers rely on these attributes to decide how to handle the linked resource. A <link> with rel="stylesheet" triggers CSS loading, while rel="icon" tells the browser to use the resource as a favicon. Without one of the required attributes, browsers may ignore the element entirely, leading to missing styles, icons, or other resources. It also affects accessibility tools and search engines that parse your markup for structured data.
Understanding the three attributes
rel— The most common attribute. It defines the relationship between your document and the linked resource. Examples includestylesheet,icon,preconnect,preload,canonical, andalternate. Most<link>elements in practice userel.itemprop— Used when the<link>element is part of an HTML Microdata structure. It specifies a property name within anitemscope, linking to a URL as the property's value. This is commonly seen with Schema.org vocabularies.property— Used with RDFa metadata (such as Open Graph tags). It defines a metadata property for the document, likeog:imageorschema:citation.
You only need one of these three attributes to satisfy the requirement, though you can combine them when appropriate.
Examples
Invalid: <link> with no relationship attribute
This triggers the validation error because the element has no rel, itemprop, or property attribute:
<head>
<title>My Page</title>
<linkhref="styles.css">
</head>
Fixed: adding rel for a stylesheet
<head>
<title>My Page</title>
<linkrel="stylesheet"href="styles.css">
</head>
Fixed: common uses of rel
<head>
<title>My Page</title>
<linkrel="stylesheet"href="styles.css">
<linkrel="icon"href="favicon.ico">
<linkrel="preconnect"href="https://fonts.googleapis.com">
<linkrel="canonical"href="https://example.com/page">
</head>
Fixed: using itemprop with Microdata
When a <link> appears inside an element with itemscope, use itemprop to define a property that takes a URL value:
<divitemscopeitemtype="https://schema.org/Article">
<h2itemprop="name">Understanding HTML Validation</h2>
<linkitemprop="mainEntityOfPage"href="https://example.com/article">
</div>
Fixed: using property with RDFa / Open Graph
Open Graph meta tags for social sharing commonly use the property attribute. While <meta> is more typical for Open Graph, <link> with property is valid for URL-type values:
<head>
<title>My Page</title>
<linkproperty="og:image"href="https://example.com/image.jpg">
<linkproperty="schema:citation"href="https://example.com/source.html">
</head>
Invalid: typo or misplaced attribute
Sometimes this error appears because of a misspelled attribute name:
<head>
<title>My Page</title>
<linkrел="stylesheet"href="styles.css">
</head>
Double-check that rel is spelled correctly and isn't accidentally omitted when copying markup from templates or code snippets.
Quick fix checklist
- Linking to a stylesheet, icon, font, or other resource? Add the appropriate
relvalue. - Defining Microdata properties? Use
itempropwithin anitemscopecontext. - Adding RDFa or Open Graph metadata? Use
propertywith the correct vocabulary prefix. - Still seeing the error? Check for typos in the attribute name or ensure the attribute isn't accidentally empty.
The <meta> element provides metadata about the HTML document — information that isn't displayed on the page but is used by browsers, search engines, and other web services. According to the HTML specification, a <meta> tag without any of the recognized attributes is meaningless. The validator flags this because a bare <meta> element (or one with only unrecognized attributes) provides no useful metadata and likely indicates an error or incomplete tag.
This issue commonly occurs when a <meta> tag is left empty by accident, when an attribute name is misspelled (e.g., naem instead of name), or when a required attribute is accidentally deleted during editing.
Most <meta> use cases fall into a few patterns, each requiring specific attribute combinations:
charset— Used alone to declare the document's character encoding.name+content— Used together to define named metadata like descriptions, viewport settings, or author information.http-equiv+content— Used together to simulate an HTTP response header.property+content— Used together for Open Graph and similar RDFa-based metadata.itemprop+content— Used together for microdata annotations.
Note that content alone is not sufficient — it must be paired with name, http-equiv, property, or itemprop to have meaning.
Examples
Incorrect: bare <meta> tag with no attributes
This triggers the validation error because the <meta> element has no recognized attributes:
<meta>
Incorrect: misspelled attribute
A typo in the attribute name means the validator doesn't recognize it:
<metanane="description"content="An example page.">
Incorrect: content without a pairing attribute
The content attribute alone is not enough — it needs name, http-equiv, property, or itemprop:
<metacontent="some value">
Correct: character encoding with charset
<metacharset="UTF-8">
Correct: named metadata with name and content
<metaname="description"content="A brief description of the webpage.">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<metaname="author"content="Jane Doe">
Correct: HTTP-equivalent with http-equiv and content
<metahttp-equiv="X-UA-Compatible"content="IE=edge">
Correct: Open Graph metadata with property and content
<metaproperty="og:title"content="My Page Title">
<metaproperty="og:description"content="A summary of the page content.">
Correct: microdata with itemprop and content
<metaitemprop="name"content="Product Name">
Full document example
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<metaname="description"content="A brief description of the webpage.">
<metaproperty="og:title"content="My Page Title">
<title>Example Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
How to fix
- Find the flagged
<meta>tag in your HTML source at the line number the validator reports. - Check for typos in attribute names — make sure
name,charset,http-equiv,property, oritempropis spelled correctly. - Add the missing attribute. Determine what the
<meta>tag is supposed to do and add the appropriate attribute(s). If you can't determine its purpose, it may be safe to remove it entirely. - Ensure proper pairing. If you're using
content, make sure it's paired withname,http-equiv,property, oritemprop. Thecharsetattribute is the only one that works on its own withoutcontent.
The <meta> element is used to provide metadata about an HTML document. According to the HTML specification, a <meta> element must serve a specific purpose, and that purpose is determined by its attributes. A bare <meta> tag or one with only a charset attribute in the wrong context will trigger this validation error.
There are several valid patterns for <meta> elements:
name+content: Standard metadata pairs (e.g., description, viewport, author).http-equiv+content: Pragma directives that affect how the browser processes the page.charset: Declares the document's character encoding (only valid once, in the<head>).itemprop+content: Microdata metadata, which can appear in both<head>and<body>.property+content: Used for Open Graph and RDFa metadata.
When a <meta> tag doesn't match any of these valid patterns, the validator raises this error. The most common causes are:
- Forgetting the
contentattribute when usingnameorproperty. - Using non-standard attributes without the required ones (e.g., only specifying a custom attribute).
- Placing a
charsetmeta in the<body>, where it's not valid. - Typos in attribute names like
contentsinstead ofcontent.
This matters for standards compliance and can also affect SEO and social sharing. Search engines and social media crawlers rely on properly formed <meta> tags to extract page information. Malformed tags may be silently ignored, meaning your metadata won't take effect.
Examples
Incorrect: <meta> with name but no content
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaname="description">
</head>
The <meta name="description"> tag is missing its content attribute, so the validator reports the error.
Correct: <meta> with both name and content
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaname="description"content="A brief description of the page.">
</head>
Incorrect: <meta> with property but no content
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaproperty="og:title">
</head>
Correct: Open Graph <meta> with property and content
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaproperty="og:title"content="My Page">
</head>
Incorrect: <meta> with only a non-standard attribute
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaname="theme-color"value="#ff0000">
</head>
Here, value is not a valid attribute for <meta>. The correct attribute is content.
Correct: Using content instead of value
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaname="theme-color"content="#ff0000">
</head>
Incorrect: Bare <meta> tag with no meaningful attributes
<head>
<metacharset="utf-8">
<title>My Page</title>
<meta>
</head>
A <meta> element with no attributes serves no purpose and should be removed entirely.
Correct: Using itemprop in the <body>
The itemprop attribute allows <meta> to be used within the <body> as part of microdata:
<body>
<divitemscopeitemtype="https://schema.org/Product">
<spanitemprop="name">Example Product</span>
<metaitemprop="sku"content="12345">
</div>
</body>
The <meta> element is used to provide machine-readable metadata about an HTML document, such as its description, character encoding, viewport settings, or social media information. The HTML specification defines several valid forms for <meta>, and most of them require a content attribute to supply the metadata's value.
This error typically appears when a <meta> tag includes a name or http-equiv attribute but is missing the corresponding content attribute. It can also appear when a <meta> tag has no recognizable attributes at all, or when the property attribute (used by Open Graph / RDFa metadata) is present without content.
A <meta> element must use one of these valid attribute patterns:
name+content— Named metadata (e.g., description, author, viewport)http-equiv+content— Pragma directives (e.g., refresh, content-type)charset— Character encoding declaration (nocontentneeded)property+content— RDFa/Open Graph metadata (e.g.,og:title)itemprop+content— Microdata metadata
Without the proper combination, browsers and search engines cannot correctly interpret the metadata, which can hurt SEO, accessibility, and proper page rendering. For example, a <meta name="description"> tag without content provides no description to search engines, and a <meta name="viewport"> without content won't configure the viewport on mobile devices.
Examples
❌ Missing content attribute
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaname="description">
<metaname="viewport">
</head>
Both <meta> tags with name are missing their required content attribute.
❌ Empty or bare <meta> tag
<head>
<metacharset="utf-8">
<title>My Page</title>
<meta>
</head>
A <meta> element with no attributes at all is invalid.
❌ Open Graph tag missing content
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaproperty="og:title">
</head>
✅ Correct usage with name and content
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaname="description"content="A brief description of the page">
<metaname="viewport"content="width=device-width, initial-scale=1">
</head>
✅ Correct usage with http-equiv and content
<metahttp-equiv="refresh"content="30">
✅ Correct usage with Open Graph property and content
<metaproperty="og:title"content="My Page Title">
<metaproperty="og:description"content="A description for social sharing">
✅ Correct charset declaration (no content needed)
<metacharset="utf-8">
The charset form is the one exception where content is not required, because the character encoding is specified directly in the charset attribute value.
How to fix
- Find the flagged
<meta>tag in your HTML source at the line number reported by the validator. - Determine what type of metadata it represents. Does it have a
name,http-equiv, orpropertyattribute? - Add the missing
contentattribute with an appropriate value. If you intended the metadata to be empty, usecontent="", though it's generally better to either provide a meaningful value or remove the tag entirely. - If the
<meta>tag has no attributes at all, decide what metadata you intended to provide and add the correct attribute combination, or remove the element.
The <meta> element is most commonly used inside the <head> section to define metadata like character encoding, viewport settings, or descriptions. Inside <head>, attributes like charset, http-equiv, and name are perfectly valid. However, the HTML specification also allows <meta> to appear inside the <body> — but only under specific conditions.
When a <meta> element appears in the <body>, it must have either an itemprop attribute (for microdata) or a property attribute (for RDFa). It must also have a content attribute. Additionally, it cannot use http-equiv, charset, or name attributes in this context. These rules exist because the only valid reason to place a <meta> tag in the <body> is to embed machine-readable metadata as part of a structured data annotation — not to define document-level metadata.
Why this matters
- Standards compliance: The HTML living standard explicitly restricts which attributes
<meta>can use depending on its placement. Violating this produces invalid HTML. - Browser behavior: Browsers may ignore or misinterpret
<meta>elements that appear in the<body>without proper attributes. For example, a<meta http-equiv="content-type">tag inside the<body>will have no effect on character encoding, since that must be determined before the body is parsed. - SEO and structured data: Search engines rely on correctly structured microdata and RDFa. A
<meta>element in the body withoutitemproporpropertywon't contribute to any structured data and serves no useful purpose.
Common causes
- Misplaced
<meta>tags: A<meta>element meant for the<head>(such as<meta http-equiv="...">or<meta name="description">) has accidentally been placed inside the<body>. This can happen due to an unclosed<head>tag, a CMS inserting tags in the wrong location, or simply copying markup into the wrong section. - Missing
itemproporproperty: A<meta>element inside the<body>is being used for structured data but is missing the requireditemproporpropertyattribute.
Examples
Incorrect: <meta> with http-equiv inside the <body>
This <meta> tag belongs in the <head>, not the <body>:
<body>
<metahttp-equiv="content-type"content="text/html; charset=UTF-8">
<form>
<inputtype="text"name="q">
</form>
</body>
Fixed: Move the <meta> to the <head>
<head>
<metahttp-equiv="content-type"content="text/html; charset=UTF-8">
<title>My Page</title>
</head>
<body>
<form>
<inputtype="text"name="q">
</form>
</body>
Incorrect: <meta> in the <body> without itemprop or property
<divitemscopeitemtype="https://schema.org/Offer">
<spanitemprop="price">9.99</span>
<metacontent="USD">
</div>
The <meta> element is missing the itemprop attribute, so the validator reports the error.
Fixed: Add the itemprop attribute
<divitemscopeitemtype="https://schema.org/Offer">
<spanitemprop="price">9.99</span>
<metaitemprop="priceCurrency"content="USD">
</div>
Correct: Using property for RDFa
The property attribute is also valid for <meta> elements in the <body> when using RDFa:
<divvocab="https://schema.org/"typeof="Event">
<spanproperty="name">Concert</span>
<metaproperty="startDate"content="2025-08-15T19:00">
</div>
Incorrect: <meta name="..."> inside the <body>
The name attribute is only valid on <meta> elements inside the <head>:
<body>
<metaname="author"content="Jane Doe">
<p>Welcome to my site.</p>
</body>
Fixed: Move it to the <head>
<head>
<title>My Site</title>
<metaname="author"content="Jane Doe">
</head>
<body>
<p>Welcome to my site.</p>
</body>
When the validator parses your HTML (especially in XHTML mode or when serialized as XML), every element name must conform to the XML 1.0 naming rules. These rules require that element names begin with a letter (a–z, A–Z) or an underscore (_), followed by any combination of letters, digits, hyphens (-), underscores, periods (.), or combining characters. Characters like spaces, angle brackets, slashes, or other special symbols within a tag name make it unrepresentable in XML 1.0.
This error most commonly occurs due to:
- Typos in tag names — accidentally inserting a space, extra character, or symbol into a tag name.
- Malformed closing tags — forgetting the slash or placing characters incorrectly in a closing tag.
- Template syntax errors — template engine placeholders leaking into the final HTML output.
- Copy-paste issues — invisible or non-ASCII characters sneaking into tag names from rich-text editors.
This matters because browsers may not parse malformed tags as intended, leading to broken layouts or missing content. Screen readers and assistive technologies rely on well-formed markup to interpret page structure. Additionally, any system that processes your HTML as XML (such as RSS feed generators, EPUB renderers, or XHTML-serving environments) will reject documents with invalid element names entirely.
How to Fix
- Inspect the flagged line — look carefully at the element name the validator is complaining about. Check for stray characters, spaces, or symbols.
- Correct any typos — replace the malformed tag with the correct HTML element name.
- Validate template output — if you use a templating engine, ensure the rendered HTML doesn't contain unprocessed template tokens inside tag names.
- Check for invisible characters — paste the tag name into a plain-text editor or use a hex viewer to spot hidden characters.
Examples
Typo with a space in the tag name
A space inside the tag name creates an invalid element name:
<!-- Wrong: space in the element name -->
<divclass="container">
<p>Hello world</p>
</di v>
Fix by removing the accidental space:
<!-- Correct -->
<divclass="container">
<p>Hello world</p>
</div>
Special character in a tag name
An accidental special character makes the name unrepresentable in XML 1.0:
<!-- Wrong: stray hash character in the tag name -->
<s#ection>
<h2>About</h2>
</s#ection>
Fix by using the correct element name:
<!-- Correct -->
<section>
<h2>About</h2>
</section>
Malformed closing tag
A missing or misplaced slash can produce a garbled tag name:
<!-- Wrong: slash is in the wrong place -->
<p>Some text<p/>
Fix with a properly formed closing tag:
<!-- Correct -->
<p>Some text</p>
Template placeholder leaking into output
Unprocessed template syntax can produce invalid element names in the rendered HTML:
<!-- Wrong: unresolved template variable in element name -->
<{{tagName}}>Content</{{tagName}}>
Ensure your template engine resolves the variable before serving the HTML. The rendered output should be:
<!-- Correct: after template processing -->
<article>Content</article>
The <object> element embeds external resources such as images, videos, PDFs, or other media into a page. The data attribute specifies the URL of the resource, while the type attribute declares its MIME type (e.g., "application/pdf", "image/svg+xml", "video/mp4"). According to the HTML specification, the element must have at least one of these attributes present — without either, the element is meaningless because the browser cannot determine what to fetch or how to render it.
While the validator requires at least one of these attributes, best practice is to include both. Here's why:
- Without
data, the browser has no resource to load. - Without
type, the browser must guess the content type from the server response, which can lead to incorrect rendering or security issues. - With both, the browser knows exactly what to fetch and how to handle it before the resource even begins downloading, improving performance and reliability.
Including both attributes also benefits accessibility. Assistive technologies use the type attribute to determine how to present the content to users. Without it, screen readers and other tools may not be able to convey useful information about the embedded resource. You should also always provide fallback content inside the <object> element for browsers or devices that cannot render the embedded resource.
Examples
Missing both attributes (triggers the error)
<objectwidth="600"height="400">
<p>Fallback content here.</p>
</object>
The validator reports this error because neither data nor type is present. The browser has no information about what this <object> should display.
Missing type attribute (valid but not recommended)
<objectdata="example.pdf"width="600"height="400">
<p>Your browser does not support PDFs. <ahref="example.pdf">Download the PDF</a>.</p>
</object>
This passes validation because data is present, but the browser must determine the content type on its own. Adding type is strongly recommended.
Missing data attribute (valid but limited)
<objecttype="application/pdf"width="600"height="400">
<p>No PDF to display.</p>
</object>
This also passes validation since type is present, but without data there is no resource to load. This pattern is uncommon and generally not useful on its own.
Correct: both attributes provided
<objectdata="example.pdf"type="application/pdf"width="600"height="400">
<p>Your browser does not support PDFs. <ahref="example.pdf">Download the PDF</a>.</p>
</object>
Correct: embedding an SVG image
<objectdata="diagram.svg"type="image/svg+xml"width="300"height="200">
<imgsrc="diagram.png"alt="Architecture diagram showing the system components">
</object>
This example also demonstrates good fallback practice — if the browser cannot render the SVG via <object>, it falls back to a regular <img> element with descriptive alt text.
Correct: embedding a video
<objectdata="intro.mp4"type="video/mp4"width="640"height="360">
<p>Your browser cannot play this video. <ahref="intro.mp4">Download it instead</a>.</p>
</object>
Quick reference of common MIME types
| Resource type | type value |
|---|---|
| PDF document | application/pdf |
| SVG image | image/svg+xml |
| MP4 video | video/mp4 |
| HTML page | text/html |
| Flash (legacy) | application/x-shockwave-flash |
To resolve this validation error, ensure every <object> element includes at least a data or type attribute. For the best results across browsers and assistive technologies, always provide both along with meaningful fallback content inside the element.
The <option> element represents a choice within a <select> dropdown, a <datalist>, or an <optgroup>. According to the HTML specification, every option must have a name — the text that is displayed to the user. This name can come from one of two sources: the text content between the opening <option> and closing </option> tags, or the label attribute on the element. If neither is provided, the option renders as a blank entry in the dropdown, which creates several problems.
From an accessibility standpoint, screen readers rely on the option's label to announce each choice to the user. An empty, unlabeled option gives assistive technology nothing meaningful to read, making the control unusable for some users. From a usability perspective, sighted users see a blank line in the dropdown and have no idea what it represents. And from a standards compliance perspective, the HTML specification explicitly requires that an <option> without a label attribute must not have empty content.
Note that the value attribute is separate from the display text. The value is what gets submitted with the form, while the label/text content is what the user sees. Setting a value does not satisfy the requirement for visible text.
How to fix it
You have two options:
- Add text content inside the
<option>element (the most common and recommended approach). - Add a
labelattribute to the<option>element. When present, thelabelattribute takes precedence over the text content for display purposes in many browsers.
If you intend for an option to serve as a placeholder (e.g., "Choose one…"), make sure it still has visible text content.
Examples
❌ Empty option without a label
This triggers the validation error because the <option> elements have no text content and no label attribute:
<selectname="size">
<optionvalue=""></option>
<optionvalue="s"></option>
<optionvalue="m"></option>
<optionvalue="l"></option>
</select>
✅ Fix: Add text content inside each option
<selectname="size">
<optionvalue="">Choose a size</option>
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
✅ Fix: Use the label attribute
The label attribute provides the display text. The element content can then be empty or differ from the label:
<selectname="size">
<optionvalue=""label="Choose a size"></option>
<optionvalue="s"label="Small"></option>
<optionvalue="m"label="Medium"></option>
<optionvalue="l"label="Large"></option>
</select>
✅ Mixing both approaches
You can use text content for some options and the label attribute for others. You can even use both on the same element — in that case, the label attribute typically takes precedence for display:
<selectname="size">
<optionvalue="">Choose a size</option>
<optionvalue="s">Small</option>
<optionvalue="m"label="Medium">Medium (M)</option>
<optionvalue="l"label="Large">Large (L)</option>
</select>
❌ Common mistake: Assuming value counts as a label
This is still invalid because value is not displayed to the user:
<selectname="color">
<optionvalue="red"></option>
</select>
✅ Corrected
<selectname="color">
<optionvalue="red">Red</option>
</select>
In almost all cases, placing readable text directly inside the <option> tags is the simplest and most compatible approach. Reserve the label attribute for situations where you need the displayed text to differ from the element's content.
The <picture> element is a container that lets you provide multiple image sources for different viewport sizes, resolutions, or format support. It works together with zero or more <source> elements and exactly one <img> element. The browser evaluates each <source> in order, selects the best match based on its media, type, or srcset attributes, and then displays the chosen image in the space occupied by the <img> element. If no <source> matches—or if the browser doesn't support the <picture> element at all—the <img> element's src attribute is used as the final fallback.
The <img> element isn't optional; it's structurally required by the HTML specification. Without it, the <picture> element is incomplete and invalid. This matters for several reasons:
- Rendering: Browsers rely on the
<img>element to actually display the image. A<picture>element with only<source>children will render nothing in most browsers. - Accessibility: The
<img>element carries thealtattribute, which provides a text alternative for screen readers and other assistive technologies. Without it, there's no accessible description of the image. - Fallback support: Older browsers that don't understand
<picture>or<source>will ignore those elements entirely and fall back to the<img>. Without it, those users see nothing. - Standards compliance: The WHATWG HTML specification explicitly states that the
<picture>element's content model requires one<img>element, optionally preceded by<source>elements and inter-element whitespace.
The <img> element should always be the last child inside <picture>, placed after all <source> elements.
Examples
Invalid: <picture> without an <img> element
This markup triggers the validation error because the required <img> child is missing:
<picture>
<sourcesrcset="hero-large.webp"type="image/webp">
<sourcesrcset="hero-large.jpg"type="image/jpeg">
</picture>
Fixed: Adding the required <img> element
Add an <img> as the last child with a src pointing to the default image and a descriptive alt attribute:
<picture>
<sourcesrcset="hero-large.webp"type="image/webp">
<sourcesrcset="hero-large.jpg"type="image/jpeg">
<imgsrc="hero-large.jpg"alt="A panoramic view of the mountain landscape">
</picture>
Using <picture> for responsive images with media queries
When serving different image sizes based on viewport width, the <img> element provides the default (typically the smallest) image:
<picture>
<sourcesrcset="banner-wide.jpg"media="(min-width: 1200px)">
<sourcesrcset="banner-medium.jpg"media="(min-width: 600px)">
<imgsrc="banner-small.jpg"alt="Promotional banner for summer sale">
</picture>
The browser checks each <source> in order. If the viewport is 1200 pixels or wider, banner-wide.jpg is used. If it's between 600 and 1199 pixels, banner-medium.jpg is used. Otherwise, the <img> element's src value of banner-small.jpg is displayed.
Using <picture> for format selection
You can also use <picture> to offer modern image formats with a fallback for browsers that don't support them:
<picture>
<sourcesrcset="photo.avif"type="image/avif">
<sourcesrcset="photo.webp"type="image/webp">
<imgsrc="photo.jpg"alt="Close-up of a sunflower in bloom">
</picture>
Browsers that support AVIF will use the first source, those that support WebP will use the second, and all others will fall back to the JPEG specified in the <img> element. In every case, the <img> element is what makes the <picture> element valid and functional.
The async attribute tells the browser to download and execute a script without blocking HTML parsing. For external scripts (those with a src attribute), this means the browser can continue parsing the page while fetching the file, then execute the script as soon as it's available. For inline module scripts (type="module"), async changes how the module's dependency graph is handled — the module and its imports execute as soon as they're all ready, rather than waiting for HTML parsing to complete.
For a classic inline script (no src, no type="module"), there is nothing to download asynchronously. The browser encounters the code directly in the HTML and executes it immediately. Applying async in this context is meaningless and contradicts the HTML specification, which is why the W3C validator flags it as an error.
Beyond standards compliance, using async incorrectly can signal a misunderstanding of script loading behavior, which may lead to bugs. For example, a developer might mistakenly believe that async on an inline script will defer its execution, when in reality it has no effect and the script still runs synchronously during parsing.
How to Fix
You have several options depending on your intent:
- If the script should be external, move the code to a separate file and reference it with the
srcattribute alongsideasync. - If the script should be an inline module, add
type="module"to the<script>tag. Note that module scripts are deferred by default, andasyncmakes them execute as soon as their dependencies are resolved rather than waiting for parsing to finish. - If the script is a plain inline script, simply remove the
asyncattribute — it has no practical effect anyway.
Examples
❌ Invalid: async on a classic inline script
<scriptasync>
console.log("Hello, world!");
</script>
This triggers the validator error because there is no src attribute and the type is not "module".
✅ Fixed: Remove async from the inline script
<script>
console.log("Hello, world!");
</script>
✅ Fixed: Use async with an external script
<scriptasyncsrc="app.js"></script>
The async attribute is valid here because the browser needs to fetch app.js from the server, and async controls when that downloaded script executes relative to parsing.
✅ Fixed: Use async with an inline module
<scriptasynctype="module">
import{greet}from"./utils.js";
greet();
</script>
This is valid because module scripts have a dependency resolution phase that can happen asynchronously. The async attribute tells the browser to execute the module as soon as all its imports are resolved, without waiting for the document to finish parsing.
❌ Invalid: async with type="text/javascript" (not a module)
<scriptasynctype="text/javascript">
console.log("This is still invalid.");
</script>
Even though type is specified, only type="module" satisfies the requirement. The value "text/javascript" is the default classic script type and does not make async valid on an inline script.
The charset attribute on the <script> element tells the browser what character encoding to use when interpreting the referenced external script file. When a script is written directly inside the HTML document (an inline script), the script's character encoding is inherently the same as the document's encoding — there is no separate file to decode. Because of this, the HTML specification requires that charset only appear on <script> elements that also have a src attribute pointing to an external file.
Including charset without src violates the HTML specification and signals a misunderstanding of how character encoding works for inline scripts. Validators flag this because browsers ignore the charset attribute on inline scripts, which means it has no effect and could mislead developers into thinking they've set the encoding when they haven't.
It's also worth noting that the charset attribute on <script> is deprecated in the HTML living standard, even for external scripts. The modern best practice is to serve external script files with the correct Content-Type HTTP header (e.g., Content-Type: application/javascript; charset=utf-8) or to simply ensure all your files use UTF-8 encoding, which is the default. If you're working with an older codebase that still uses charset, consider removing it entirely and relying on UTF-8 throughout.
Examples
Incorrect: charset on an inline script
This triggers the validation error because charset is specified without a corresponding src attribute.
<scriptcharset="utf-8">
console.log("Hello, world!");
</script>
Correct: Remove charset from inline scripts
Since inline scripts use the document's encoding, simply remove the charset attribute.
<script>
console.log("Hello, world!");
</script>
Correct: Use charset with an external script (deprecated but valid)
If you need to specify the encoding of an external script, both charset and src must be present. Note that this usage, while valid, is deprecated.
<scriptsrc="app.js"charset="utf-8"></script>
Recommended: External script without charset
The preferred modern approach is to omit charset entirely and ensure the server delivers the file with the correct encoding header, or simply use UTF-8 for everything.
<scriptsrc="app.js"></script>
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