HTML Guides
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The HTML living standard defines a content model for the <a> element that explicitly excludes interactive content from appearing as descendants. Interactive content includes elements like <button>, <input>, <select>, <textarea>, and other <a> elements. When you nest an <input> inside a link, browsers face an ambiguous situation: should a click activate the link or interact with the input? Different browsers may handle this differently, leading to inconsistent behavior.
This restriction also matters for accessibility. Screen readers and other assistive technologies rely on a clear, predictable DOM structure. Nesting interactive elements creates confusion for users navigating with keyboards or screen readers, as the focus order and interaction model become unclear. A user tabbing through the page might not understand that an input lives inside a link, or they might be unable to interact with one of the two elements.
Common scenarios where this issue arises include wrapping a search input in a link to make the entire area clickable, or placing a checkbox inside a link to combine selection with navigation. In all cases, the solution is to separate the interactive elements.
Examples
❌ Incorrect: <input> inside an <a> element
<ahref="/search">
<inputtype="text"placeholder="Search...">
</a>
This triggers the validation error because <input> is interactive content nested inside <a>.
✅ Correct: Separate the elements
<formaction="/search">
<inputtype="text"placeholder="Search...">
<buttontype="submit">Search</button>
</form>
If the goal is to navigate to a search page, use a <form> with an action attribute instead of wrapping the input in a link.
❌ Incorrect: Checkbox inside a link
<ahref="/settings">
<inputtype="checkbox"id="notify"> Enable notifications
</a>
✅ Correct: Place the link and input as siblings
<label>
<inputtype="checkbox"id="notify"> Enable notifications
</label>
<ahref="/settings">Go to settings</a>
✅ Correct: Use styling to achieve a clickable area
If you want a visually combined area where clicking navigates somewhere, avoid using an <input> altogether and style the link instead:
<ahref="/search"class="search-link">
<span>Search...</span>
</a>
Alternatively, if you need both a link and an input near each other, use CSS layout to position them visually together while keeping them as separate elements in the markup:
<divclass="search-bar">
<inputtype="text"placeholder="Search...">
<ahref="/search">Go</a>
</div>
❌ Incorrect: Hidden input inside a link
Even hidden or non-visible inputs trigger this error:
<ahref="/page">
<inputtype="hidden"name="ref"value="home">
Click here
</a>
✅ Correct: Move the hidden input outside the link
<inputtype="hidden"name="ref"value="home">
<ahref="/page">Click here</a>
If the hidden input is meant to pass data during navigation, consider using query parameters in the link's href instead:
<ahref="/page?ref=home">Click here</a>
The th element already carries semantic meaning as a table header cell. Nesting a heading element like h4 inside it creates a conflict in the document's outline and semantic structure. Screen readers and other assistive technologies treat headings and table headers as distinct navigational landmarks, so combining them can confuse users who rely on these tools to understand page structure. A heading buried inside a table cell may break the expected heading hierarchy, making it harder for users to navigate by headings.
According to the HTML specification, the content model of th is "flow content, but with no header, footer, sectioning content, or heading content descendants." This means h1, h2, h3, h4, h5, and h6 are all explicitly disallowed inside th.
The reason developers often place headings inside th is to achieve a specific visual style — larger, bolder text. But th elements are already rendered bold by default in most browsers, and any additional styling should be handled with CSS rather than repurposing heading elements.
How to Fix It
- Remove the heading element from inside the
thand use the text directly. - Style with CSS if you need the
thcontent to look different from default styling. - Move the heading outside the table if it serves as a title or caption for the table. Consider using the
<caption>element for table titles.
Examples
❌ Incorrect: Heading inside th
<table>
<tr>
<th><h4>Product</h4></th>
<th><h4>Price</h4></th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
This triggers the validation error because h4 elements are not allowed as descendants of th.
✅ Fixed: Plain text in th
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
The simplest fix — just remove the heading tags. The th element already conveys that these cells are headers.
✅ Fixed: Using CSS for custom styling
If you need the header cells to have a specific visual appearance, use CSS:
<table>
<tr>
<thclass="styled-header">Product</th>
<thclass="styled-header">Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
<style>
.styled-header{
font-size:1.2em;
text-transform: uppercase;
}
</style>
✅ Fixed: Moving the heading outside and using caption
If the heading was meant to serve as a title for the table, use the <caption> element instead:
<table>
<caption>Product Pricing</caption>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
The <caption> element is purpose-built for labeling tables and is well-supported by assistive technologies. You can also place a heading before the table if it fits your document's heading hierarchy:
<h4>Product Pricing</h4>
<table>
<tr>
<th>Product</th>
<th>Price</th>
</tr>
<tr>
<td>Widget</td>
<td>$25</td>
</tr>
</table>
Both approaches keep your HTML valid while preserving clear semantics for both visual users and assistive technology.
The <iframe> element embeds an entirely separate HTML document within the current page, creating its own independent browsing context. The <a> element, on the other hand, is an interactive element designed to navigate users to a new URL or location. When you nest an <iframe> inside an <a>, browsers face a conflict: user interactions like clicks could be intended for the embedded content inside the iframe or for the link itself. The HTML specification resolves this ambiguity by simply disallowing it.
According to the WHATWG HTML living standard, the <a> element's content model does not permit interactive content as descendants. The <iframe> element is categorized as interactive content, which means it must not appear anywhere inside an <a> tag — not as a direct child and not nested deeper within other elements inside the link.
This restriction matters for several reasons:
- Accessibility: Screen readers and assistive technologies cannot reliably convey the purpose of a link that contains an embedded document. Users may not understand whether they are interacting with the link or the iframe.
- Unpredictable behavior: Different browsers may handle clicks on the iframe-inside-a-link differently, leading to inconsistent user experiences.
- Standards compliance: Violating the content model makes your HTML invalid, which can cause unexpected rendering and behavior.
To fix the issue, restructure your markup so the <iframe> and <a> are siblings or otherwise separated. If your goal is to provide a link alongside embedded content, place the link before or after the iframe. If you want a clickable preview that links somewhere, consider using an image thumbnail inside the link instead of an iframe.
Examples
❌ Invalid: <iframe> inside an <a> element
<ahref="https://example.com">
<iframesrc="https://example.com/embed"></iframe>
</a>
This triggers the validation error because the <iframe> is a descendant of the <a> element.
❌ Invalid: <iframe> nested deeper inside an <a> element
<ahref="https://example.com">
<div>
<iframesrc="https://example.com/embed"></iframe>
</div>
</a>
Even though the <iframe> is not a direct child, it is still a descendant of the <a> element, which is not allowed.
✅ Valid: Separate the <iframe> and <a> elements
<ahref="https://example.com">Visit Example.com</a>
<iframesrc="https://example.com/embed"></iframe>
The link and the iframe are siblings, so there is no nesting conflict.
✅ Valid: Use an image as a clickable preview instead
If the intent is to create a clickable preview that links to a page, use a thumbnail image rather than an iframe:
<ahref="https://example.com">
<imgsrc="preview-thumbnail.jpg"alt="Preview of Example.com">
</a>
✅ Valid: Wrap in a container with a separate link
If you need both an iframe and a related link displayed together, use a wrapper element:
<divclass="embed-container">
<iframesrc="https://example.com/embed"title="Embedded content from Example.com"></iframe>
<p><ahref="https://example.com">Open Example.com in a new page</a></p>
</div>
Note that when using <iframe>, it's good practice to include a title attribute to describe the embedded content for accessibility purposes.
The <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.
In HTML5, the only namespace-related attribute permitted on the <html> element is xmlns with the value http://www.w3.org/1999/xhtml, and even that is optional—it exists solely for compatibility with XHTML serialization. Prefixed namespace declarations like xmlns:w, xmlns:o, xmlns:v, and similar attributes are XML features that have no meaning in the HTML syntax. They originate from Microsoft Office's HTML export, which generates markup containing proprietary XML namespaces such as urn:schemas-microsoft-com:office:word for Word-specific elements and styling.
Why This Is a Problem
Standards compliance: The HTML living standard (WHATWG) explicitly does not support custom namespace declarations. Including them makes your document non-conforming, and the W3C validator will report an error for each one.
No browser benefit: Modern browsers parsing HTML5 ignore these namespace declarations entirely. The attributes serve no functional purpose on the web—they only add unnecessary bloat to your markup.
Maintenance and readability: Office-generated HTML is notoriously verbose. Leaving namespace declarations in place often goes hand-in-hand with other Office artifacts (conditional comments, <o:p> tags, mso- style properties) that clutter your code and make it harder to maintain.
Accessibility and interoperability: While browsers typically tolerate these extra attributes without visible issues, non-browser HTML consumers—such as screen readers, search engine crawlers, or email clients—may handle unexpected attributes unpredictably.
How to Fix It
- Remove the
xmlns:wattribute from your<html>tag or any other element where it appears. - Remove related namespace declarations like
xmlns:o(Office),xmlns:v(VML), andxmlns:m(Math) if present. - Clean up Office-specific elements such as
<w:Sdt>,<o:p>, or<v:shape>that depend on those namespaces—these are not valid HTML elements. - Strip Office-specific CSS properties prefixed with
mso-(e.g.,mso-bidi-font-family) that often accompany namespace declarations.
If you regularly paste content from Microsoft Word, consider using a "paste as plain text" feature in your editor, or use an HTML cleaning tool to strip Office artifacts automatically.
Examples
Invalid: Office namespace on the <html> element
<!DOCTYPE html>
<htmllang="en"xmlns:w="urn:schemas-microsoft-com:office:word">
<head>
<title>Document</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Invalid: Multiple Office namespaces
<!DOCTYPE html>
<htmllang="en"
xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Document</title>
</head>
<body>
<pclass="MsoNormal">Content from Word<o:p></o:p></p>
</body>
</html>
Valid: Clean HTML without namespace declarations
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Document</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Valid: Using the standard xmlns attribute (optional)
If you need XHTML compatibility, the standard xmlns attribute (without a prefix) is permitted:
<!DOCTYPE html>
<htmllang="en"xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Document</title>
</head>
<body>
<p>Content from Word</p>
</body>
</html>
Note that this same validation error applies to any custom prefixed namespace attribute—not just xmlns:w. If you see similar errors for xmlns:o, xmlns:v, xmlns:st1, or others, the fix is the same: remove them along with any elements or attributes that depend on those namespaces.
The HTML5 specification strictly limits which attributes are valid on the <html> element—and on elements in general. Standard global attributes like lang, dir, class, and id are permitted, and in XHTML serialization the plain xmlns attribute is allowed to declare the default XHTML namespace. However, prefixed namespace declarations like xmlns:m, xmlns:o, xmlns:v, or xmlns:w are XML constructs that have no meaning in the HTML5 (text/html) parsing model.
These prefixed namespace attributes most often appear when content is generated by or copied from Microsoft Office products (Word, Excel, PowerPoint). Office uses custom XML namespaces such as xmlns:m for Office MathML (http://schemas.microsoft.com/office/2004/12/omml) and xmlns:o for Office-specific markup. When this markup is saved as HTML or pasted into a web page, these declarations come along and trigger validation errors.
Why This Is a Problem
- Standards compliance: The W3C HTML5 specification does not support custom XML namespace declarations in the
text/htmlserialization. Validators will flag every such attribute. - No functional benefit: HTML5 parsers ignore namespace prefixes entirely. The
xmlns:mdeclaration does nothing in a browser rendering an HTML5 page, so it is dead code. - Content bloat: Office-generated HTML often includes many unnecessary namespace declarations, inline styles, and proprietary elements that bloat the document and make it harder to maintain.
- Accessibility and interoperability: Clean, valid HTML is easier for assistive technologies, search engines, and other user agents to process reliably.
How to Fix It
- Remove the custom namespace attribute from the
<html>element (or whichever element it appears on). - Remove any elements or attributes that depend on that namespace prefix (e.g.,
<m:oMath>,<o:p>), since they are not valid HTML5 elements. - Replace with HTML5-native equivalents where possible. For example, MathML is natively supported in HTML5 without any namespace declaration—you can use
<math>elements directly. - If you truly need XML namespaces, serve your document as XHTML with the
application/xhtml+xmlcontent type instead oftext/html.
Examples
Incorrect: Custom namespace on the html element
This triggers the "Attribute xmlns:m not allowed here" error:
<!DOCTYPE html>
<htmlxmlns:m="http://schemas.microsoft.com/office/2004/12/omml"
xmlns:o="urn:schemas-microsoft-com:office:office"
lang="en">
<head>
<title>Office Paste Example</title>
</head>
<body>
<p>Some content with Office markup.</p>
</body>
</html>
Correct: Clean HTML5 without namespace declarations
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Clean HTML5 Example</title>
</head>
<body>
<p>Some content without Office markup.</p>
</body>
</html>
Correct: Using MathML natively in HTML5
If the xmlns:m namespace was being used for mathematical content, HTML5 supports MathML directly without any namespace declaration:
<!DOCTYPE html>
<htmllang="en">
<head>
<title>MathML in HTML5</title>
</head>
<body>
<p>The quadratic formula:</p>
<math>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mo>-</mo>
<mi>b</mi>
<mo>±</mo>
<msqrt>
<mrow>
<msup><mi>b</mi><mn>2</mn></msup>
<mo>-</mo>
<mn>4</mn><mi>a</mi><mi>c</mi>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>2</mn><mi>a</mi>
</mrow>
</mfrac>
</math>
</body>
</html>
Incorrect: Namespace attribute on a non-html element
The same error can appear on other elements too:
<divxmlns:o="urn:schemas-microsoft-com:office:office">
<p>Office content</p>
</div>
Correct: Remove the namespace attribute
<div>
<p>Office content</p>
</div>
If you're cleaning up Office-generated HTML, consider using a dedicated tool or a "paste as plain text" option in your CMS to strip out proprietary markup before it enters your pages. This keeps your HTML lean, valid, and maintainable.
In HTML5, SVG elements are natively supported and don't require explicit namespace declarations at all. When you write an <svg> tag inside an HTML document, the browser automatically associates it with the correct SVG namespace (http://www.w3.org/2000/svg). The xmlns:svg="http://www.w3.org/2000/svg" attribute is a prefixed namespace binding — it declares svg as a namespace prefix — which is a concept from XML/XHTML workflows that HTML5's parser does not support or recognize.
The HTML specification defines a limited set of allowed attributes on each element. Since xmlns:svg is not among them for the <svg> element (or any HTML5 element), the W3C validator flags it as invalid. The only namespace-related attribute the HTML parser tolerates on <svg> is xmlns="http://www.w3.org/2000/svg", and even that is optional — it's accepted purely for compatibility with XHTML but has no functional effect in HTML5.
This issue commonly appears when SVG code is exported from graphic editors like Inkscape or Adobe Illustrator, or when SVG files originally written as standalone XML documents are pasted directly into HTML. These tools sometimes include verbose namespace declarations that are necessary in XML contexts but invalid in HTML.
Why it matters
- Standards compliance: The HTML5 specification explicitly does not allow prefixed namespace attributes. Using them results in validation errors.
- Code cleanliness: Unnecessary attributes add clutter without any benefit, making your markup harder to read and maintain.
- Potential parsing issues: While most browsers silently ignore unrecognized attributes, relying on lenient browser behavior rather than valid markup can lead to unexpected results in edge cases or non-browser HTML consumers (e.g., email clients, content scrapers, or accessibility tools).
How to fix it
- Locate all
<svg>elements in your HTML that contain thexmlns:svgattribute. - Remove the
xmlns:svg="http://www.w3.org/2000/svg"attribute entirely. - If you're working in an HTML5 document (not XHTML), the
xmlns="http://www.w3.org/2000/svg"attribute is also optional — you can keep or remove it. - If your SVG uses other prefixed namespace declarations like
xmlns:xlink, consider replacing them with their HTML5 equivalents (e.g., usehrefinstead ofxlink:href).
Examples
Incorrect: using xmlns:svg on an SVG element
The xmlns:svg attribute triggers the validation error:
<svgxmlns="http://www.w3.org/2000/svg"xmlns:svg="http://www.w3.org/2000/svg"viewBox="0 0 100 100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
Correct: removing the prefixed namespace
Remove xmlns:svg and keep only the standard attributes:
<svgxmlns="http://www.w3.org/2000/svg"viewBox="0 0 100 100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
Also correct: minimal inline SVG in HTML5
In HTML5, the xmlns attribute itself is optional for inline SVG, so this is the cleanest approach:
<svgviewBox="0 0 100 100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
Cleaning up multiple unnecessary namespaces
SVG exported from editors often includes several unnecessary namespace declarations. Here's a before-and-after of a typical cleanup:
Before (multiple invalid namespace attributes):
<svgxmlns="http://www.w3.org/2000/svg"xmlns:svg="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"viewBox="0 0 200 200">
<imagexlink:href="photo.jpg"width="200"height="200"/>
</svg>
After (cleaned up for HTML5):
<svgviewBox="0 0 200 200">
<imagehref="photo.jpg"width="200"height="200"/>
</svg>
Note that xlink:href has been replaced with href, which is the modern standard supported by all current browsers. The xmlns:xlink declaration is no longer needed either.
The xmlns:dt attribute — short for "XML Namespace: datatypes" — was historically used in Microsoft-specific XML vocabularies (notably urn:schemas-microsoft-com:datatypes) to declare data type information on elements. It was common in older ASP and IE-era markup. However, HTML5 is not an XML language, and it does not support arbitrary XML namespace declarations on elements.
In HTML5, the only xmlns attribute permitted is xmlns="http://www.w3.org/1999/xhtml" on the <html> element itself, and even that exists solely for compatibility with XHTML serialization. Namespace-prefixed attributes like xmlns:dt, xmlns:o, or xmlns:v are invalid. The HTML parser simply does not recognize them, and the W3C validator will flag them with the error "Attribute 'xmlns:dt' not allowed here."
Why This Is a Problem
- Standards compliance: Using non-standard attributes means your document does not conform to the HTML specification, which can lead to unpredictable behavior across browsers.
- Legacy lock-in: The
xmlns:dtattribute is tied to Microsoft's proprietary data type schema. Modern browsers do not interpret or use this namespace, so it serves no functional purpose in an HTML5 document. - Validation noise: Invalid attributes generate validator errors that can obscure real issues in your markup, making it harder to catch genuine bugs.
- Accessibility and tooling: Screen readers, search engine crawlers, and other automated tools expect valid HTML. Non-standard attributes can confuse parsers or be silently discarded.
How to Fix It
- Remove the attribute. If
xmlns:dtwas carried over from legacy code or a CMS template and nothing in your application depends on it, simply delete it. - Replace with
data-*attributes. If you need to attach custom metadata to an element — for example, to indicate a data type for use by JavaScript — use an HTML5data-*attribute instead. - Use XHTML if XML namespaces are required. If you genuinely need XML namespace support (rare in modern web development), serve your document as
application/xhtml+xmlwith a proper XHTML doctype and XML declaration. Be aware this changes parsing rules significantly.
Examples
Incorrect: Using xmlns:dt in HTML5
This will trigger the validation error:
<ulxmlns:dt="urn:schemas-microsoft-com:datatypes">
<lidt:dt="string">Item one</li>
<lidt:dt="number">42</li>
</ul>
Both xmlns:dt on the <ul> and the dt:dt attributes on the <li> elements are invalid in HTML5.
Correct: Attribute removed
If the namespace declaration is unnecessary (which it almost always is in modern HTML), remove it along with any prefixed attributes:
<ul>
<li>Item one</li>
<li>42</li>
</ul>
Correct: Using data-* attributes for custom metadata
If your JavaScript or application logic needs to know the data type of each item, use valid data-* attributes:
<ul>
<lidata-type="string">Item one</li>
<lidata-type="number">42</li>
</ul>
You can then access these values in JavaScript with element.dataset.type.
Correct: Migrating a legacy div wrapper
A common legacy pattern places xmlns:dt on a container div:
<!-- Invalid -->
<divxmlns:dt="urn:schemas-microsoft-com:datatypes">
<spandt:dt="dateTime">2024-01-15</span>
</div>
The fixed version removes the namespace and uses standard attributes:
<div>
<timedatetime="2024-01-15">January 15, 2024</time>
</div>
In this case, the semantic <time> element with its datetime attribute is the proper HTML5 way to represent date and time values — no custom namespace needed.
Quick Checklist
- Search your codebase for
xmlns:dtand anydt:prefixed attributes. - Check CMS templates, server-generated markup, and copy-pasted legacy code — these are the most common sources.
- Remove the attributes or replace them with
data-*equivalents. - Re-validate your document to confirm the error is resolved.
The xmlns:fb attribute was an XML namespace declaration used to enable FBML (Facebook Markup Language) tags on websites. FBML allowed developers to embed Facebook-specific elements like <fb:like>, <fb:comments>, and <fb:share-button> directly in their HTML. Facebook officially retired FBML in 2011 and replaced it with the JavaScript SDK and social plugins, yet many websites still carry this outdated namespace declaration in their markup.
In HTML5, the only xmlns attribute allowed on the <html> element is the standard xmlns="http://www.w3.org/1999/xhtml", and even that is optional. Custom namespace prefixes like xmlns:fb or xmlns:og are XML constructs that have no meaning in HTML5 and are flagged as validation errors.
Why This Is a Problem
- Standards compliance: HTML5 does not support arbitrary XML namespace declarations. The
xmlns:fbattribute violates the HTML specification, producing a validation error. - Dead technology: FBML no longer functions. Facebook's servers no longer process FBML tags, so the namespace serves no purpose whatsoever.
- Code cleanliness: Keeping deprecated, non-functional attributes in your markup adds confusion for developers maintaining the codebase and suggests the site hasn't been updated in a long time.
How to Fix It
- Remove
xmlns:fb(and any other custom namespace likexmlns:og) from your<html>tag. - Remove any FBML tags such as
<fb:like>,<fb:comments>, or<fb:share-button>from your page content. - Replace with modern alternatives:
- Use the Facebook JavaScript SDK for social plugins.
- Use Open Graph
<meta>tags in the<head>to control how your pages appear when shared on Facebook. These<meta>tags use thepropertyattribute (e.g.,property="og:title") and do not require any namespace declaration in HTML5.
Examples
❌ Invalid: Using xmlns:fb on the <html> tag
<!DOCTYPE html>
<htmllang="en"xmlns:fb="http://ogp.me/ns/fb#">
<head>
<title>My Page</title>
</head>
<body>
<fb:likehref="https://example.com"width="300"></fb:like>
</body>
</html>
This triggers the error "Attribute 'xmlns:fb' not allowed here" because HTML5 does not permit custom XML namespace prefixes on the <html> element. The <fb:like> tag is also invalid HTML and no longer functional.
✅ Valid: Using Open Graph meta tags and the JavaScript SDK
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
<metaproperty="og:title"content="My Page">
<metaproperty="og:description"content="A description of my page.">
<metaproperty="og:url"content="https://example.com">
<metaproperty="og:image"content="https://example.com/image.jpg">
</head>
<body>
<h1>My Page</h1>
<!-- Facebook Like button using the JavaScript SDK -->
<divclass="fb-like"data-href="https://example.com"data-width="300"data-layout="button_count"></div>
<scriptasyncdefercrossorigin="anonymous"
src="https://connect.facebook.net/en_US/sdk.js#xfbml=1&version=v18.0">
</script>
</body>
</html>
The xmlns:fb attribute is gone, Open Graph metadata is provided via standard <meta> tags, and the like button is rendered using Facebook's JavaScript SDK with data-* attributes—all fully valid HTML5.
Custom XML namespace declarations like xmlns:dc are not allowed on <svg> elements in HTML5 documents.
In HTML5 (as opposed to XHTML), the parser only recognizes a limited set of namespace attributes on SVG elements: xmlns and xmlns:xlink. Any other namespace declarations, such as xmlns:dc, xmlns:cc, xmlns:rdf, or xmlns:svg, are invalid. These namespaces are commonly added by SVG editors like Inkscape but serve no purpose in an HTML5 context.
The SVG metadata that relies on these namespaces (like Dublin Core dc: elements or Creative Commons cc: elements) is also typically unnecessary when embedding SVGs in HTML. You can safely remove these namespace declarations and their associated metadata elements without affecting how the SVG renders.
Invalid Example
<svgxmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100">
<metadata>
<rdf:RDF>
<cc:Workrdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:title>My Icon</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
Valid Example
<svgxmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
Remove all non-standard xmlns: attributes and the <metadata> block they support. If you're exporting from a tool like Inkscape, look for a "plain SVG" or "optimized SVG" export option, or use a tool like SVGO to clean up the output automatically.
In HTML5, the only namespace attributes recognized on SVG elements are xmlns (for the SVG namespace) and xmlns:xlink (for XLink, though xlink is now largely deprecated in favor of plain attributes). Any other custom namespace declarations — such as xmlns:serif, xmlns:inkscape, or xmlns:sketch — are not permitted by the HTML specification and will trigger a validation error.
Design tools like Affinity Designer, Adobe Illustrator, Inkscape, and Sketch often embed proprietary namespace declarations and metadata attributes in exported SVG files. These serve as internal markers for the design application (for example, to preserve layer names or editor-specific settings) but have no meaning in a web browser. Browsers simply ignore them, so they add unnecessary bytes to your page without providing any benefit.
Removing these attributes is important for several reasons:
- Standards compliance: The HTML5 parser has a fixed list of allowed namespace declarations. Custom ones violate the spec.
- Clean markup: Proprietary metadata bloats your SVG files with information that only matters inside the originating design tool.
- Maintainability: Removing tool-specific artifacts makes your SVGs easier to read, edit, and optimize.
To fix this, open your SVG file (or the HTML file containing inline SVGs) and remove the xmlns:serif attribute from the <svg> element. You should also search for and remove any attributes prefixed with serif: (such as serif:id) throughout the SVG, since those depend on the now-removed namespace declaration.
For a more automated approach, consider using SVGO or similar SVG optimization tools, which strip out editor metadata and unnecessary namespace declarations by default.
Examples
Incorrect — custom namespace attribute present
<svgxmlns="http://www.w3.org/2000/svg"
xmlns:serif="https://www.serif.com/"
viewBox="0 0 100 100">
<circleserif:id="MainCircle"cx="50"cy="50"r="40"fill="blue"/>
</svg>
This triggers two types of errors: xmlns:serif is not allowed on the <svg> element, and serif:id is not a recognized attribute on <circle>.
Correct — custom namespace and prefixed attributes removed
<svgxmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100">
<circlecx="50"cy="50"r="40"fill="blue"/>
</svg>
Incorrect — multiple proprietary namespaces
Design tools sometimes add several custom namespaces at once:
<svgxmlns="http://www.w3.org/2000/svg"
xmlns:serif="https://www.serif.com/"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 200 200">
<gserif:id="Layer1">
<rectx="10"y="10"width="80"height="80"fill="red"/>
</g>
</svg>
Correct — only standard namespaces retained
<svgxmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 200">
<g>
<rectx="10"y="10"width="80"height="80"fill="red"/>
</g>
</svg>
Note that xmlns:xlink was also removed in this example. While it won't always trigger a validation error by itself, it's unnecessary if no xlink:href attributes are used — and modern HTML5 SVG usage favors plain href over xlink:href anyway.
The xmlns:v attribute declares the XML namespace urn:schemas-microsoft-com:vml, which enabled Microsoft's proprietary Vector Markup Language in Internet Explorer versions 5 through 8. VML was a way to render vector shapes directly in HTML before SVG gained broad browser support. When working with XML-based documents (like XHTML), namespace declarations such as xmlns:v were syntactically valid. However, HTML5 is not an XML language — it has its own parsing rules and only recognizes a limited set of namespace declarations on the <html> element, specifically xmlns (for the default XHTML namespace) and xmlns:xlink (in certain SVG/MathML contexts). Any other xmlns:* attribute, including xmlns:v, xmlns:o, and xmlns:w, triggers a validation error.
Why This Is a Problem
Standards compliance: The HTML5 specification explicitly does not allow arbitrary XML namespace declarations. The W3C validator flags xmlns:v because it is not part of the HTML5 attribute set for any element.
No modern browser support: VML was only supported in Internet Explorer, which has been discontinued. No current browser renders VML content, so the namespace declaration serves no purpose.
Code cleanliness: Keeping legacy, non-functional attributes clutters your markup and can confuse developers who maintain the code. It may also cause issues with HTML parsers, linters, and build tools that enforce strict HTML5 compliance.
How to Fix It
- Remove the
xmlns:vattribute from your<html>tag (or wherever it appears). - Remove any related namespace attributes like
xmlns:o(Office namespace) orxmlns:w(Word namespace), which are also invalid in HTML5 and often appear alongsidexmlns:v. - Replace VML content with SVG if your page relied on VML for vector graphics. SVG is natively supported in all modern browsers and requires no namespace declaration on the
<html>element. - Ensure the
langattribute is set on the<html>element for accessibility, since you're already editing that line.
Examples
Invalid: Using xmlns:v on the html element
<!DOCTYPE html>
<htmlxmlns:v="urn:schemas-microsoft-com:vml"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:w="urn:schemas-microsoft-com:office:word">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
This triggers multiple validation errors — one for each xmlns:* attribute that HTML5 does not recognize.
Valid: Namespace attributes removed
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Replacing VML with SVG
If your page used VML to draw shapes, replace the VML markup with inline SVG. No namespace declaration on <html> is needed — the <svg> element carries its own namespace implicitly in HTML5.
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Vector Graphics with SVG</title>
</head>
<body>
<svgwidth="200"height="200"aria-label="Blue circle">
<circlecx="100"cy="100"r="80"fill="steelblue"/>
</svg>
</body>
</html>
Common Sources of This Issue
This attribute frequently appears in HTML that was:
- Exported from Microsoft Word or Outlook. These applications generate HTML with VML namespace declarations for shapes, text effects, and email formatting.
- Copied from legacy templates. Older website templates and email templates sometimes included VML for rounded corners or background images in Internet Explorer.
- Generated by outdated WYSIWYG editors that targeted older IE versions.
If you're working with HTML email templates, note that some email clients (notably older Outlook desktop versions) still use the Word rendering engine and may rely on VML for certain effects like background images. In that context, you may intentionally keep VML in your email source — but be aware that the markup will not pass HTML5 validation. For web pages, there is no reason to retain these attributes.
In XML and XHTML, the xmlns attribute is used to declare namespaces that allow elements and attributes from different vocabularies to coexist without naming conflicts. The xmlns:o prefix specifically declares the Microsoft Office namespace (urn:schemas-microsoft-com:office:office), which enables Office-specific elements and attributes like <o:p> (Office paragraph markers) within the document.
HTML5, however, is not an XML language. The HTML5 specification only permits the xmlns attribute on the <html> element (and only with the value http://www.w3.org/1999/xhtml), along with xmlns:xlink on SVG elements. Custom namespace prefixes like xmlns:o, xmlns:v (VML), and xmlns:w (Word) are not recognized as valid attributes on any HTML5 element and will trigger validation errors.
Why This Happens
This issue most commonly occurs when:
- Content is pasted from Microsoft Word into a WYSIWYG editor or CMS. Word generates its own flavor of HTML that includes Office namespace declarations and proprietary elements.
- HTML files are exported from Office applications like Word, Excel, or Outlook. These exports produce markup heavily reliant on Office-specific namespaces.
- Email templates are built using tools that generate Office-compatible HTML, carrying namespace baggage into standard web pages.
Why It Matters
- Standards compliance: HTML5 does not support arbitrary XML namespace declarations, so these attributes make your document invalid.
- Bloated markup: Office-generated HTML often includes not just the namespace declarations but also large amounts of Office-specific elements, conditional comments, and inline styles that increase page size significantly.
- Maintenance difficulty: Office-flavored HTML is harder to read, edit, and maintain compared to clean, standards-compliant markup.
- Potential rendering issues: While browsers generally ignore unrecognized attributes, the accompanying Office-specific elements (like
<o:p>) can occasionally cause unexpected spacing or layout behavior.
How to Fix It
- Remove all
xmlns:oattributes from your HTML elements. - Remove any Office-specific elements such as
<o:p>,<o:SmartTagType>, or similar tags that rely on the Office namespace. - Check for other Office namespaces like
xmlns:v,xmlns:w,xmlns:m, andxmlns:st1— these should also be removed. - Clean pasted content before inserting it into your HTML. Many text editors and CMS platforms offer a "Paste as plain text" option that strips out Office formatting.
- If you use a CMS or rich text editor, look for a "Clean up Word HTML" or similar feature to automatically strip Office artifacts.
Examples
❌ Invalid: Office namespace declarations on elements
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Company Report</title>
</head>
<bodyxmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="urn:schemas-microsoft-com:office:word">
<h1>Quarterly Report</h1>
<pclass="MsoNormal">Revenue increased by 15% this quarter.<o:p></o:p></p>
<pclass="MsoNormal">Expenses remained stable.<o:p></o:p></p>
</body>
</html>
This markup contains three Office namespace declarations on the <body> element and uses <o:p> elements inside paragraphs — all of which are invalid in HTML5.
✅ Valid: Clean HTML without Office namespaces
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Company Report</title>
</head>
<body>
<h1>Quarterly Report</h1>
<p>Revenue increased by 15% this quarter.</p>
<p>Expenses remained stable.</p>
</body>
</html>
❌ Invalid: Namespace on a div element
<divxmlns:o="urn:schemas-microsoft-com:office:office">
<pclass="MsoNormal">Meeting notes from Tuesday.<o:p></o:p></p>
</div>
✅ Valid: Clean version
<div>
<p>Meeting notes from Tuesday.</p>
</div>
Notice that in the corrected examples, the class="MsoNormal" attribute was also removed. While MsoNormal is technically a valid class name that won't cause a validation error, it's an Office-generated class with no purpose unless you have corresponding CSS rules — removing it keeps your markup clean.
If you genuinely need XML namespace support for document processing, use a proper XHTML document served with the application/xhtml+xml content type, or handle the XML processing separately from your web-facing HTML.
In older XHTML documents, XML namespaces were declared using the xmlns attribute with a prefix, such as xmlns:og="http://ogp.me/ns#". While this syntax was valid in XHTML, HTML5 does not support custom XML namespace declarations on the <html> element. The W3C validator will flag any xmlns:* prefixed attribute (like xmlns:og, xmlns:fb, etc.) as invalid because HTML5 has a strictly defined set of allowed attributes on the <html> element.
The Open Graph Protocol, originally developed by Facebook, allows web pages to become rich objects in a social graph. Major platforms like Facebook, Twitter, LinkedIn, and others rely on Open Graph <meta> tags to generate link previews. The official Open Graph Protocol specification recommends using the prefix attribute on the <html> element instead of the xmlns:og namespace declaration.
The prefix attribute is part of the RDFa specification and is recognized by HTML5 as a valid way to declare vocabulary prefixes. By switching to prefix="og: https://ogp.me/ns#", you maintain full Open Graph functionality while keeping your HTML valid.
How to fix it
- Locate the
<html>tag in your document. - Remove any
xmlns:og(or similarxmlns:*) attributes. - Add or update the
prefixattribute with the appropriate namespace declaration.
If you use multiple prefixes (e.g., Open Graph and Facebook-specific tags), you can combine them in a single prefix attribute, separated by a space.
Examples
❌ Invalid: Using xmlns:og (XHTML-style namespace)
<!DOCTYPE html>
<htmllang="en"xmlns:og="http://ogp.me/ns#">
<head>
<title>My Page</title>
<metaproperty="og:title"content="My Page Title"/>
<metaproperty="og:type"content="website"/>
<metaproperty="og:url"content="https://example.com/"/>
<metaproperty="og:image"content="https://example.com/image.jpg"/>
</head>
<body>
<p>Page content here.</p>
</body>
</html>
This triggers the validator error: Attribute "xmlns:og" not allowed here.
✅ Valid: Using the prefix attribute
<!DOCTYPE html>
<htmllang="en"prefix="og: https://ogp.me/ns#">
<head>
<title>My Page</title>
<metaproperty="og:title"content="My Page Title"/>
<metaproperty="og:type"content="website"/>
<metaproperty="og:url"content="https://example.com/"/>
<metaproperty="og:image"content="https://example.com/image.jpg"/>
</head>
<body>
<p>Page content here.</p>
</body>
</html>
✅ Valid: Multiple prefixes (Open Graph and Facebook)
If you also use Facebook-specific meta tags (like fb:app_id), declare both prefixes:
<!DOCTYPE html>
<htmllang="en"prefix="og: https://ogp.me/ns# fb: https://ogp.me/ns/fb#">
<head>
<title>The Rock (1996)</title>
<metaproperty="fb:app_id"content="123456789"/>
<metaproperty="og:title"content="The Rock"/>
<metaproperty="og:type"content="video.movie"/>
<metaproperty="og:url"content="https://www.imdb.com/title/tt0117500/"/>
<metaproperty="og:image"content="https://ia.media-imdb.com/images/rock.jpg"/>
</head>
<body>
<p>Page content here.</p>
</body>
</html>
❌ Invalid: Multiple xmlns declarations
This older pattern with multiple namespace declarations is equally invalid in HTML5:
<htmlxmlns:og="http://ogp.me/ns#"xmlns:fb="http://ogp.me/ns/fb#">
Replace it with the single prefix attribute as shown in the examples above.
The <a> element is an interactive content element — it's already focusable and keyboard-navigable by default. When you place an element with a tabindex attribute inside a link, you create a nested focus target. This means that keyboard users and screen readers encounter two (or more) focusable items where only one is expected. The browser may not handle this consistently, and the user experience becomes unpredictable: should pressing Enter activate the link, or the inner focusable element?
The HTML specification defines that certain interactive elements must not be nested within other interactive elements. An <a> element's content model explicitly forbids interactive content as descendants. Adding tabindex to any element makes it interactive (focusable), which violates this rule.
This matters for several reasons:
- Accessibility: Screen readers may announce nested focusable elements in confusing ways, or skip them entirely. Users relying on keyboard navigation may get trapped or confused by unexpected tab stops inside a link.
- Standards compliance: The W3C validator flags this as an error because it violates the HTML content model for anchor elements.
- Browser inconsistency: Different browsers may handle nested focusable elements differently, leading to unpredictable behavior across platforms.
To fix this issue, you have a few options:
- Remove the
tabindexattribute from the descendant element if it doesn't need to be independently focusable. - Restructure your markup so the focusable element is a sibling of the
<a>element rather than a descendant. - Rethink the design — if you need multiple interactive targets in the same area, consider using separate elements styled to appear as a single unit.
Examples
❌ Invalid: Element with tabindex inside an <a> element
<ahref="/products">
<divtabindex="0">
<h2>Our Products</h2>
<p>Browse our full catalog</p>
</div>
</a>
The <div> has tabindex="0", making it focusable inside an already-focusable <a> element. This creates conflicting focus targets.
✅ Fixed: Remove tabindex from the descendant
<ahref="/products">
<div>
<h2>Our Products</h2>
<p>Browse our full catalog</p>
</div>
</a>
Since the <a> element is already focusable and clickable, the inner <div> doesn't need its own tabindex. Removing it resolves the conflict.
❌ Invalid: span with tabindex inside a link
<ahref="/profile">
<spantabindex="-1">User Name</span>
</a>
Even tabindex="-1" (which removes the element from the natural tab order but still makes it programmatically focusable) triggers this validation error when used inside an <a> element.
✅ Fixed: Remove tabindex from the span
<ahref="/profile">
<span>User Name</span>
</a>
❌ Invalid: Button-like element nested inside a link
<ahref="/dashboard">
<divtabindex="0"role="button">Settings</div>
</a>
✅ Fixed: Separate the interactive elements
<divclass="card-actions">
<ahref="/dashboard">Dashboard</a>
<buttontype="button">Settings</button>
</div>
If you truly need two distinct interactive elements, place them as siblings rather than nesting one inside the other. Use CSS to style them as a cohesive visual unit if needed.
The HTML specification defines button as an interactive content element that accepts phrasing content as its children, but explicitly forbids interactive content as descendants. When you add a tabindex attribute to an element, you make it focusable and potentially interactive, which violates this content model restriction.
This rule exists for important reasons. A button element is a single interactive control — when a user presses Tab, the entire button receives focus as one unit. If elements inside the button also have tabindex, screen readers and keyboard users encounter nested focusable items within what should be a single action target. This creates confusing, unpredictable behavior: users may tab into the button's internals without understanding the context, and assistive technologies may announce the inner elements separately, breaking the expected interaction pattern.
Browsers may also handle nested focusable elements inconsistently. Some may ignore the inner tabindex, while others may allow focus on the nested element but not properly trigger the button's click handler, leading to broken functionality.
How to fix it
The most straightforward fix is to remove the tabindex attribute from any elements inside the button. If the inner element was given tabindex="0" to make it focusable, it doesn't need it — the button itself is already focusable. If it was given tabindex="-1" to programmatically manage focus, reconsider whether that focus management is necessary within a button context.
If you genuinely need multiple interactive elements in the same area, restructure your markup so that the interactive elements are siblings rather than nested inside a button.
Examples
❌ Incorrect: span with tabindex inside a button
<buttontype="button">
<spantabindex="0">Click me</span>
</button>
The span has tabindex="0", making it a focusable descendant of the button. This violates the content model.
✅ Correct: Remove tabindex from the descendant
<buttontype="button">
<span>Click me</span>
</button>
The span no longer has tabindex, so the button behaves as a single focusable control.
❌ Incorrect: Multiple focusable elements inside a button
<buttontype="button">
<spantabindex="0"class="icon">★</span>
<spantabindex="-1"class="label">Favorite</span>
</button>
Both inner span elements have tabindex attributes, which is invalid regardless of the tabindex value.
✅ Correct: Style inner elements without making them focusable
<buttontype="button">
<spanclass="icon">★</span>
<spanclass="label">Favorite</span>
</button>
✅ Correct: Restructure if separate interactions are needed
If you need an icon and a separate action side by side, use sibling elements instead of nesting:
<spanclass="icon"aria-hidden="true">★</span>
<buttontype="button">Favorite</button>
This keeps the button as a clean, single interactive element while placing the decorative icon outside of it.
The <main> element represents the dominant, unique content of a document — the primary content that is directly related to or expands upon the central topic of the page. Having more than one visible <main> element creates ambiguity: browsers, screen readers, and other assistive technologies use <main> to identify the primary content area, and multiple visible instances make it unclear which content block is truly the main one.
This is particularly important for accessibility. Screen reader users often rely on landmark navigation to jump directly to the main content of a page. When multiple visible <main> elements exist, this shortcut becomes unreliable or confusing, as the user has no way to know which <main> holds the content they're looking for.
There are legitimate scenarios where multiple <main> elements make sense — for example, in single-page applications (SPAs) where different views are swapped in and out dynamically using JavaScript. The HTML specification accommodates this by allowing multiple <main> elements as long as only one is visible at a time. The others must be hidden using the hidden attribute.
How to fix it
- If you only need one main content area, remove the extra
<main>elements and keep just one. - If you need multiple views (e.g., for tabbed content or SPA-style navigation), add the
hiddenattribute to all<main>elements except the one currently active. Use JavaScript to toggle visibility by adding or removing thehiddenattribute as needed.
Examples
❌ Invalid: Two visible <main> elements
<header>
<h1>My Website</h1>
</header>
<main>
<h2>Welcome</h2>
<p>This is the home page content.</p>
</main>
<main>
<h2>About</h2>
<p>This is the about page content.</p>
</main>
Both <main> elements are visible, which violates the specification and confuses assistive technologies.
✅ Fixed: Single <main> element
If you don't need multiple views, simply use one <main>:
<header>
<h1>My Website</h1>
</header>
<main>
<h2>Welcome</h2>
<p>This is the home page content.</p>
</main>
✅ Fixed: Multiple <main> elements with only one visible
If you need multiple views for JavaScript-driven navigation, hide all but the active one using the hidden attribute:
<header>
<nav>
<buttononclick="switchView('home')">Home</button>
<buttononclick="switchView('about')">About</button>
</nav>
</header>
<mainid="home">
<h2>Welcome</h2>
<p>This is the home page content.</p>
</main>
<mainid="about"hidden>
<h2>About</h2>
<p>This is the about page content.</p>
</main>
In this pattern, JavaScript would toggle the hidden attribute when the user navigates between views, ensuring only one <main> is ever visible at a time.
❌ Invalid: Using CSS instead of hidden
Note that hiding a <main> element with CSS (e.g., display: none or visibility: hidden) does not satisfy the HTML specification. The validator checks for the hidden attribute, not CSS styles:
<!-- This still triggers the validation error -->
<main>
<h2>Welcome</h2>
</main>
<mainstyle="display: none;">
<h2>About</h2>
</main>
Always use the hidden attribute to indicate that a <main> element is not currently relevant to the page.
The WAI-ARIA specification defines strict rules about which elements can be children of interactive roles. An element with role="button" is treated as an interactive control, and the <a> element (when it has an href) is also an interactive control. Nesting one interactive element inside another creates what's known as an interactive content nesting violation. Screen readers and other assistive technologies cannot reliably determine user intent when they encounter this pattern — should they announce a button, a link, or both? The result is unpredictable behavior that can make your content inaccessible.
This issue commonly appears when developers wrap links inside styled containers that have been given role="button", or when using component libraries that apply button semantics to wrapper elements containing anchor tags.
Beyond accessibility, browsers themselves handle nested interactive elements inconsistently. Some may ignore the outer interactive role, while others may prevent the inner link from functioning correctly. This makes the pattern unreliable even for users who don't rely on assistive technologies.
How to fix it
There are several approaches depending on your intent:
- If the element should navigate to a URL, use an
<a>element and style it to look like a button. Remove therole="button"from the parent or eliminate the parent wrapper entirely. - If the element should perform an action (not navigation), use a
<button>element or an element withrole="button", and handle the action with JavaScript. Remove the nested<a>tag. - If you need both a button container and a link, flatten the structure so they are siblings rather than nested.
When using role="button" on a non-button element, remember that you must also handle keyboard interaction (Enter and Space key presses) and include tabindex="0" to make it focusable.
Examples
❌ Incorrect: link nested inside a button role
<divrole="button">
<ahref="/dashboard">Go to Dashboard</a>
</div>
This triggers the validation error because the <a> is a descendant of an element with role="button".
✅ Fix 1: Use a styled link instead
If the intent is navigation, remove the button role and let the <a> element do the job. Style it to look like a button with CSS.
<ahref="/dashboard"class="btn">Go to Dashboard</a>
This is the simplest and most semantically correct fix when the purpose is to navigate the user to another page.
✅ Fix 2: Use a real button for actions
If the intent is to trigger an action (not navigate), replace the entire structure with a <button> element.
<buttontype="button"class="btn"onclick="navigateToDashboard()">
Go to Dashboard
</button>
✅ Fix 3: Use a div with role="button" without nested interactive elements
If you need a custom button using role="button", make sure it contains no interactive descendants.
<divrole="button"tabindex="0">
Go to Dashboard
</div>
When using this approach, you must also add keyboard event handlers for Enter and Space to match native button behavior.
❌ Incorrect: link inside a button element
The same principle applies to native <button> elements, not just elements with role="button":
<button>
<ahref="/settings">Settings</a>
</button>
✅ Fixed: choose one interactive element
<ahref="/settings"class="btn">Settings</a>
❌ Incorrect: deeply nested link
The error applies to any level of nesting, not just direct children:
<divrole="button">
<spanclass="icon-wrapper">
<ahref="/help">Help</a>
</span>
</div>
✅ Fixed: flatten the structure
<ahref="/help"class="btn">
<spanclass="icon-wrapper">Help</span>
</a>
As a rule of thumb, every interactive element in your page should have a single, clear role. If something looks like a button but navigates to a URL, make it an <a> styled as a button. If it performs an in-page action, make it a <button>. Keeping these roles distinct ensures your HTML is valid, accessible, and behaves consistently across browsers and assistive technologies.
The role="button" attribute tells assistive technologies like screen readers that an element behaves as a button — a widget used to perform actions such as submitting a form, opening a dialog, or triggering a command. When a <button> element appears inside an element with role="button", the result is a nested interactive control. The HTML specification explicitly forbids this because interactive content must not be nested within other interactive content.
This nesting causes real problems. Screen readers may announce the outer element as a button but fail to recognize or reach the inner <button>. Keyboard users may not be able to focus on or activate the inner control. Different browsers handle the situation inconsistently — some may ignore one of the controls entirely, others may fire events on the wrong element. The end result is an interface that is broken for many users.
This issue commonly arises in a few scenarios:
- A
<div>or<span>is givenrole="button"and then a<button>is placed inside it for styling or click-handling purposes. - A component library wraps content in a
role="button"container, and a developer adds a<button>inside without realizing the conflict. - A custom card or list item is made clickable with
role="button", but also contains action buttons within it.
The fix depends on your intent. If the outer element is the intended interactive control, remove the inner <button> and handle interactions on the outer element. If the inner <button> is the intended control, remove role="button" from the ancestor. If both need to be independently clickable, restructure the markup so neither is a descendant of the other.
Examples
❌ Incorrect: <button> inside an element with role="button"
<divrole="button"tabindex="0"onclick="handleClick()">
<buttontype="button">Click me</button>
</div>
This is invalid because the <button> is a descendant of the <div> that has role="button".
✅ Fix option 1: Use only the <button> element
If the inner <button> is the actual control, remove role="button" from the wrapper:
<div>
<buttontype="button"onclick="handleClick()">Click me</button>
</div>
✅ Fix option 2: Use only the outer role="button" element
If the outer element is the intended interactive control, remove the inner <button>:
<divrole="button"tabindex="0"onclick="handleClick()">
Click me
</div>
Note that when using role="button" on a non-<button> element, you must also handle keyboard events (Enter and Space) manually. A native <button> provides this for free, so prefer option 1 when possible.
❌ Incorrect: Clickable card containing action buttons
<divrole="button"tabindex="0"class="card">
<h3>Item title</h3>
<p>Description text</p>
<buttontype="button">Delete</button>
</div>
✅ Fix: Separate the card link from the action buttons
<divclass="card">
<h3><buttontype="button"class="card-link">Item title</button></h3>
<p>Description text</p>
<buttontype="button">Delete</button>
</div>
In this approach, the card's main action is handled by a <button> on the title, while the "Delete" button remains an independent control. Neither is nested inside the other, and both are accessible to keyboard and screen reader users.
An <a> element cannot be placed inside a <button> element because interactive content must not be nested within other interactive content.
The HTML specification forbids nesting clickable elements inside other clickable elements. Both <a> and <button> are interactive content, meaning they each expect to receive user interaction independently. When you nest one inside the other, browsers can't determine which element should handle the click, leading to unpredictable behavior and accessibility problems.
Screen readers and keyboard navigation also struggle with nested interactive elements. A user tabbing through the page may not be able to reach or activate the inner link, or may trigger the wrong action entirely.
To fix this, you have two main options: use a styled <a> element that looks like a button, or use a <button> with JavaScript to handle navigation.
Invalid Example
<button>
<ahref="/dashboard">Go to Dashboard</a>
</button>
Fixed Examples
Option 1: Style the link as a button
This is the preferred approach when the purpose is navigation.
<ahref="/dashboard"class="btn">Go to Dashboard</a>
<style>
.btn{
display: inline-block;
padding:8px16px;
background-color:#007bff;
color:#fff;
text-decoration: none;
border-radius:4px;
border: none;
cursor: pointer;
}
</style>
Option 2: Use a button with JavaScript
This works when you need button semantics but also want navigation.
<buttontype="button"onclick="location.href='/dashboard'">
Go to Dashboard
</button>
Option 1 is generally better for navigation because <a> elements communicate the correct intent to assistive technologies and allow standard browser behaviors like right-click "Open in new tab."
The HTML specification defines strict rules about which elements can be nested inside others. The <a> element is classified as interactive content, and its content model explicitly forbids other interactive content as descendants. Elements with role="button" — whether a native <button> or any element with the ARIA role — are also interactive. Nesting one inside the other creates an ambiguous situation: should a click activate the link or the button?
This matters for several important reasons:
- Accessibility: Screen readers and assistive technologies rely on a clear, unambiguous element hierarchy. When a button is inside a link, the announced role becomes confusing — users may hear both a link and a button, or the assistive technology may only expose one of them, hiding the other's functionality.
- Unpredictable behavior: Browsers handle nested interactive elements inconsistently. Some may split the elements apart in the DOM, while others may swallow click events. This leads to broken or unreliable behavior across different browsers.
- Standards compliance: The WHATWG HTML Living Standard and W3C HTML specification both explicitly prohibit this nesting pattern.
To fix this issue, decide what the element should actually do. If it navigates to a URL, use an <a> element. If it performs an action (like submitting a form or triggering JavaScript), use a <button>. If you need both visual styles, use CSS to style one element to look like the other.
Examples
❌ Incorrect: Element with role="button" inside an <a>
<ahref="/dashboard">
<spanrole="button">Go to Dashboard</span>
</a>
The <span> with role="button" is interactive content nested inside the interactive <a> element.
❌ Incorrect: <button> inside an <a>
<ahref="/settings">
<button>Open Settings</button>
</a>
A <button> element is inherently interactive and must not appear inside an <a>.
✅ Correct: Use a link styled as a button
If the purpose is navigation, use the <a> element and style it with CSS:
<ahref="/dashboard"class="btn">Go to Dashboard</a>
✅ Correct: Use a button that navigates via JavaScript
If you need a button that also navigates, handle navigation in JavaScript:
<buttontype="button"onclick="location.href='/dashboard'">Go to Dashboard</button>
✅ Correct: Place elements side by side
If you truly need both a link and a button, keep them as siblings rather than nesting one inside the other:
<divclass="actions">
<ahref="/dashboard">Go to Dashboard</a>
<buttontype="button">Save Preference</button>
</div>
✅ Correct: Link styled as a button using ARIA (when appropriate)
If the element navigates but you want assistive technologies to announce it as a button, you can apply role="button" directly to the element — just don't nest it inside an <a>:
<spanrole="button"tabindex="0"onclick="location.href='/dashboard'">
Go to Dashboard
</span>
Note that using role="button" on a non-interactive element like <span> requires you to also add tabindex="0" for keyboard focusability and handle keydown events for the Enter and Space keys. In most cases, a native <a> or <button> element is the better choice.
The ARIA button role tells browsers and assistive technologies that an element behaves like a button. According to the WAI-ARIA specification, elements with role="button" follow the same content restrictions as native <button> elements. Specifically, they must not contain interactive content as descendants. The <input> element is considered interactive content, so nesting it inside any element with role="button" is invalid.
This restriction exists for important accessibility and usability reasons. When a screen reader encounters an element with role="button", it announces it as a single actionable control. If that button contains another interactive element like an <input>, the user faces conflicting interactions — should activating the element trigger the button action or interact with the input? This ambiguity confuses both assistive technologies and users. Browsers may also handle focus and click events unpredictably when interactive elements are nested this way.
The same rule applies to native <button> elements, <a> elements with an href, and any other element that is already interactive. Adding role="button" to a <div> or <span> elevates it to the same status, so the same nesting restrictions apply.
To fix this issue, consider these approaches:
- Move the
<input>outside the button-role element and position them as siblings. - Replace the
<input>with non-interactive content such as a<span>styled to look like the desired control, with appropriate ARIA attributes to convey state. - Rethink the component structure — if you need both a button action and an input, they should be separate controls that are visually grouped but not nested.
Examples
❌ Invalid: <input> nested inside an element with role="button"
<divrole="button"tabindex="0">
<inputtype="checkbox"/>
Accept terms
</div>
❌ Invalid: <input> nested inside a native <button>
<button>
<inputtype="text"/>
Search
</button>
✅ Valid: Separate the <input> and button into sibling elements
<label>
<inputtype="checkbox"/>
Accept terms
</label>
<button>Submit</button>
✅ Valid: Use non-interactive content inside the button-role element
If you want a toggle-style button that conveys checked/unchecked state, use ARIA attributes on the button itself instead of embedding an <input>:
<divrole="button"tabindex="0"aria-pressed="false">
<spanaria-hidden="true">☐</span>
Accept terms
</div>
✅ Valid: Use a <label> and <input> alongside a button
<div>
<label>
<inputtype="checkbox"/>
Accept terms
</label>
<divrole="button"tabindex="0">Continue</div>
</div>
✅ Valid: Button with only non-interactive phrasing content
<divrole="button"tabindex="0">
<span>Click me</span>
</div>
When in doubt, keep interactive elements as separate, distinct controls rather than nesting them. This ensures clear semantics, predictable behavior across browsers, and a good experience for users of assistive technologies.
The itemtype and itemscope attributes are part of the HTML Microdata specification, which allows you to embed structured, machine-readable data into your HTML. The itemscope attribute creates a new item — it defines a scope within which properties (via itemprop) are associated. The itemtype attribute then specifies a vocabulary URL (typically from Schema.org) that describes what kind of item it is.
According to the WHATWG HTML Living Standard, itemtype has no meaning without itemscope. The itemscope attribute is what establishes the element as a microdata item container. Without it, itemtype has nothing to qualify — there is no item to assign a type to. This is why the spec requires itemscope to be present whenever itemtype is used.
Getting this wrong matters for several reasons:
- Structured data won't work. Search engines like Google rely on valid microdata to generate rich results (e.g., star ratings, event details, product prices). Invalid markup means your structured data will be silently ignored.
- Standards compliance. Using
itemtypewithoutitemscopeviolates the HTML specification, and validators will flag it as an error. - Maintainability. Other developers (or your future self) may assume the microdata is functioning correctly when it isn't.
To fix this issue, you have two options:
- Add
itemscopeto the element — this is the correct fix if you intend to use microdata. - Remove
itemtype— this is appropriate if you don't actually need structured data on that element.
Examples
Incorrect — itemtype without itemscope
This triggers the validation error because itemscope is missing:
<divitemtype="https://schema.org/Person">
<p><spanitemprop="name">Liza Jane</span></p>
<p><spanitemprop="email">liza.jane@example.com</span></p>
</div>
Correct — adding itemscope alongside itemtype
Adding the itemscope attribute establishes the element as a microdata item, making itemtype valid:
<divitemscopeitemtype="https://schema.org/Person">
<p><spanitemprop="name">Liza Jane</span></p>
<p><spanitemprop="email">liza.jane@example.com</span></p>
</div>
Here, itemscope tells parsers that this div contains a microdata item, and itemtype="https://schema.org/Person" specifies that the item is a Person with properties like name and email.
Correct — removing itemtype when structured data isn't needed
If you don't need typed structured data, simply remove the itemtype attribute. You can still use itemscope on its own to create an untyped item, or remove both attributes entirely:
<div>
<p><span>Liza Jane</span></p>
<p><span>liza.jane@example.com</span></p>
</div>
Correct — nested items with itemscope and itemtype
When nesting microdata items, each level that uses itemtype must also have itemscope:
<divitemscopeitemtype="https://schema.org/Organization">
<spanitemprop="name">Acme Corp</span>
<divitemprop="founder"itemscopeitemtype="https://schema.org/Person">
<spanitemprop="name">Liza Jane</span>
</div>
</div>
Notice that both the outer div (the Organization) and the inner div (the Person) have itemscope paired with their respective itemtype values. Omitting itemscope from either element would trigger the validation error.
The name attribute on an <input> element identifies the form control's data when the form is submitted. It acts as the key in the key-value pair sent to the server (e.g., email=user@example.com). When you set name="", the attribute is present but contains an empty string, which the HTML specification considers an invalid value. An empty name means the input's data will be excluded from the form's submission payload in most browsers, making it functionally useless for form processing.
This issue matters for several reasons:
- Form functionality: Inputs with empty names are typically omitted from form data, so the server never receives the user's input.
- Standards compliance: The HTML specification requires that if the
nameattribute is present, its value must not be empty. - JavaScript references: An empty
namemakes it difficult to reference the element using methods likedocument.getElementsByName()orFormData. - Accessibility: Screen readers and assistive technologies may use the
nameattribute to help identify form controls, and an empty value provides no useful information.
Note that the name attribute is not technically required on every <input> element — it's perfectly valid to omit it entirely. For example, inputs used purely for client-side JavaScript interactions without form submission don't need a name. The error specifically arises when the attribute is present but set to an empty string.
To fix the issue, either assign a meaningful name that describes the data the input collects, or remove the name attribute altogether if the input isn't part of a form submission.
Examples
❌ Empty name attribute triggers the error
<formaction="/submit"method="post">
<labelfor="email">Email:</label>
<inputtype="email"id="email"name="">
<buttontype="submit">Submit</button>
</form>
✅ Providing a meaningful name value
<formaction="/submit"method="post">
<labelfor="email">Email:</label>
<inputtype="email"id="email"name="email">
<buttontype="submit">Submit</button>
</form>
✅ Removing name when the input isn't submitted
If the input is only used for client-side interaction and doesn't need to be part of form data, simply omit the attribute:
<labelfor="search">Filter results:</label>
<inputtype="text"id="search">
❌ Multiple inputs with empty names
This pattern sometimes appears when inputs are generated dynamically with placeholder attributes:
<formaction="/register"method="post">
<inputtype="text"name="">
<inputtype="password"name="">
<buttontype="submit">Register</button>
</form>
✅ Each input gets a descriptive name
<formaction="/register"method="post">
<labelfor="username">Username:</label>
<inputtype="text"id="username"name="username">
<labelfor="password">Password:</label>
<inputtype="password"id="password"name="password">
<buttontype="submit">Register</button>
</form>
The <link> element is used to define relationships between the current document and external resources—most commonly stylesheets, favicons, and preloaded assets. It is a void element (it has no closing tag) and it produces no rendered content on the page. Because <link> elements are inherently non-visible and already absent from the accessibility tree, the aria-hidden attribute serves no purpose on them.
The aria-hidden attribute is designed to control the visibility of rendered content for assistive technologies like screen readers. When set to "true" on a visible element, it tells assistive technologies to skip that element and its descendants. Applying it to a <link> element is contradictory—you're trying to hide something from assistive technologies that was never exposed to them in the first place. The HTML specification explicitly disallows this combination, and the W3C validator will flag it as an error.
This issue sometimes arises when developers apply aria-hidden broadly through templating systems, JavaScript frameworks, or build tools that inject attributes across multiple element types without distinguishing between visible content elements and metadata elements. It can also happen when copying attribute patterns from <a> elements (which share the word "link" conceptually but are entirely different elements) onto <link> elements.
How to fix it
The fix is straightforward: remove the aria-hidden attribute from the <link> element. No replacement or alternative attribute is needed because <link> elements are already invisible to assistive technologies.
If your original intent was to hide a visible element from screen readers, make sure you're applying aria-hidden to the correct element—a rendered content element such as <div>, <span>, <img>, or <a>, not a metadata element like <link>.
Examples
Incorrect: aria-hidden on a <link> element
<linkrel="stylesheet"href="styles.css"aria-hidden="true">
<linkrel="icon"href="favicon.ico"aria-hidden="true">
<linkrel="preload"href="font.woff2"as="font"type="font/woff2"aria-hidden="true">
Correct: <link> without aria-hidden
<linkrel="stylesheet"href="styles.css">
<linkrel="icon"href="favicon.ico">
<linkrel="preload"href="font.woff2"as="font"type="font/woff2">
Correct: aria-hidden used on a visible element instead
If you need to hide a decorative element from screen readers, apply aria-hidden to the rendered element itself:
<linkrel="stylesheet"href="styles.css">
<divaria-hidden="true">
<imgsrc="decorative-swirl.png"alt="">
</div>
Incorrect vs. correct in a full document
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
<!-- Incorrect: aria-hidden on link -->
<linkrel="stylesheet"href="styles.css"aria-hidden="true">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
<!-- Correct: no aria-hidden on link -->
<linkrel="stylesheet"href="styles.css">
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
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