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 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:dt attribute 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:dt was 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 HTML5 data-* 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+xml with 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:
<ul xmlns:dt="urn:schemas-microsoft-com:datatypes">
<li dt:dt="string">Item one</li>
<li dt: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>
<li data-type="string">Item one</li>
<li data-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 -->
<div xmlns:dt="urn:schemas-microsoft-com:datatypes">
<span dt:dt="dateTime">2024-01-15</span>
</div>
The fixed version removes the namespace and uses standard attributes:
<div>
<time datetime="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:dt and any dt: 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:fb attribute 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 like xmlns: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 the property attribute (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>
<html lang="en" xmlns:fb="http://ogp.me/ns/fb#">
<head>
<title>My Page</title>
</head>
<body>
<fb:like href="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>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
<meta property="og:title" content="My Page">
<meta property="og:description" content="A description of my page.">
<meta property="og:url" content="https://example.com">
<meta property="og:image" content="https://example.com/image.jpg">
</head>
<body>
<h1>My Page</h1>
<!-- Facebook Like button using the JavaScript SDK -->
<div class="fb-like" data-href="https://example.com" data-width="300" data-layout="button_count"></div>
<script async defer crossorigin="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.
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/html serialization. Validators will flag every such attribute.
- No functional benefit: HTML5 parsers ignore namespace prefixes entirely. The xmlns:m declaration 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+xml content type instead of text/html.
Examples
Incorrect: Custom namespace on the html element
This triggers the “Attribute xmlns:m not allowed here” error:
<!DOCTYPE html>
<html xmlns: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>
<html lang="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>
<html lang="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:
<div xmlns: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 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:o attributes 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, and xmlns: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>
<html lang="en">
<head>
<title>Company Report</title>
</head>
<body xmlns: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>
<p class="MsoNormal">Revenue increased by 15% this quarter.<o:p></o:p></p>
<p class="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>
<html lang="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
<div xmlns:o="urn:schemas-microsoft-com:office:office">
<p class="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 similar xmlns:*) attributes.
- Add or update the prefix attribute 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>
<html lang="en" xmlns:og="http://ogp.me/ns#">
<head>
<title>My Page</title>
<meta property="og:title" content="My Page Title" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://example.com/" />
<meta property="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>
<html lang="en" prefix="og: https://ogp.me/ns#">
<head>
<title>My Page</title>
<meta property="og:title" content="My Page Title" />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://example.com/" />
<meta property="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>
<html lang="en" prefix="og: https://ogp.me/ns# fb: https://ogp.me/ns/fb#">
<head>
<title>The Rock (1996)</title>
<meta property="fb:app_id" content="123456789" />
<meta property="og:title" content="The Rock" />
<meta property="og:type" content="video.movie" />
<meta property="og:url" content="https://www.imdb.com/title/tt0117500/" />
<meta property="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:
<html xmlns: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.
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
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:serif="https://www.serif.com/"
viewBox="0 0 100 100">
<circle serif: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
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
Incorrect — multiple proprietary namespaces
Design tools sometimes add several custom namespaces at once:
<svg xmlns="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">
<g serif:id="Layer1">
<rect x="10" y="10" width="80" height="80" fill="red" />
</g>
</svg>
Correct — only standard namespaces retained
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 200">
<g>
<rect x="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.
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 the xmlns:svg attribute.
- 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., use href instead of xlink:href).
Examples
Incorrect: using xmlns:svg on an SVG element
The xmlns:svg attribute triggers the validation error:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
Correct: removing the prefixed namespace
Remove xmlns:svg and keep only the standard attributes:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<circle cx="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:
<svg viewBox="0 0 100 100">
<circle cx="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):
<svg xmlns="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">
<image xlink:href="photo.jpg" width="200" height="200" />
</svg>
After (cleaned up for HTML5):
<svg viewBox="0 0 200 200">
<image href="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: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:v attribute from your <html> tag (or wherever it appears).
- Remove any related namespace attributes like xmlns:o (Office namespace) or xmlns:w (Word namespace), which are also invalid in HTML5 and often appear alongside xmlns: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 lang attribute 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>
<html xmlns: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>
<html lang="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>
<html lang="en">
<head>
<title>Vector Graphics with SVG</title>
</head>
<body>
<svg width="200" height="200" aria-label="Blue circle">
<circle cx="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 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:w attribute from your <html> tag or any other element where it appears.
- Remove related namespace declarations like xmlns:o (Office), xmlns:v (VML), and xmlns: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>
<html lang="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>
<html lang="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>
<p class="MsoNormal">Content from Word<o:p></o:p></p>
</body>
</html>
Valid: Clean HTML without namespace declarations
<!DOCTYPE html>
<html lang="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>
<html lang="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 < character is the opening delimiter for HTML tags. When the browser’s HTML parser encounters <, it expects a valid tag name to follow (like div, p, span, etc.). If it instead finds an unexpected character—a digit, a symbol, a space, or anything that doesn’t form a recognized tag—the parser enters an error state. The validator flags this because the < was almost certainly meant as literal content, not as the start of a tag.
This issue commonly appears in several scenarios:
- Mathematical or comparison expressions like x < y or if (count < 10)
- Inline code or technical content that includes angle brackets, such as explaining generics (List<String>) or template syntax
- User-generated content or CMS output where text hasn’t been properly HTML-encoded
- Copy-pasting code snippets directly into HTML without escaping
Beyond triggering validation errors, unescaped < characters can cause real rendering problems. The browser may try to interpret the text following < as a tag, swallowing part of your visible content or producing unexpected DOM structures. This can also create accessibility issues, as screen readers rely on a well-formed DOM to convey content accurately.
How to fix it
Replace every < that appears as content (not as part of an actual HTML tag) with the HTML entity <. Similarly, replace literal > with >. While > doesn’t always cause validation errors, escaping both characters is a best practice that prevents ambiguity and keeps your markup clean.
If you’re writing code examples, wrap them in <code> or <pre> elements—but note that you still need to escape < and > inside those elements. The <pre> and <code> tags preserve whitespace and indicate code semantics, but they do not disable HTML parsing.
Examples
❌ Bad: unescaped < in a math expression
<p>Check if x < 10 before proceeding.</p>
The parser sees < 10 and tries to interpret 10 as a tag name, triggering the error.
✅ Fixed: escaped with <
<p>Check if x < 10 before proceeding.</p>
❌ Bad: unescaped angle brackets in a code snippet
<pre><code>
List<String> names = new ArrayList<String>();
</code></pre>
The parser interprets <String> as an unknown HTML tag, breaking the content.
✅ Fixed: all angle brackets escaped inside <code>
<pre><code>
List<String> names = new ArrayList<String>();
</code></pre>
❌ Bad: unescaped < in a conditional expression
<p>The expression 1 < 2 && 3 > 1 evaluates to true.</p>
✅ Fixed: both < and > escaped
<p>The expression 1 < 2 && 3 > 1 evaluates to true.</p>
Note that && also needs to be escaped as && because & is itself a reserved character in HTML.
Quick reference for common HTML entities
| Character | Entity | Description |
|---|---|---|
| < | < | Less-than sign |
| > | > | Greater-than sign |
| & | & | Ampersand |
| " | " | Double quotation mark |
If you’re generating HTML dynamically (via JavaScript, a server-side language, or a CMS), make sure your output pipeline includes an HTML-encoding step. Most frameworks and template engines provide a built-in escaping function—use it for any user-supplied or variable content that gets inserted into HTML.
The <noscript> element behaves differently depending on where it appears in a document. When placed inside the <head>, it can only contain <link>, <style>, and <meta> elements — strictly metadata content. When placed inside the <body>, it can contain any flow content, including <p>, <div>, <iframe>, and more. This distinction is defined in the WHATWG HTML Living Standard.
When the validator encounters an <iframe> inside a <noscript> in the <head>, it reports “Bad start tag” because the parser is operating under the <head> content model, where <iframe> is simply not a valid element. The browser may attempt error recovery by implicitly closing the <head> and opening the <body>, but this can lead to unexpected DOM structures and layout issues.
This pattern commonly appears when adding tracking or analytics snippets. For instance, Google Tag Manager provides a <noscript> fallback containing an <iframe>, and it’s meant to be placed immediately after the opening <body> tag — not in the <head>. Placing it in the wrong location breaks validation and may cause the tracking pixel to malfunction.
To fix the error, identify any <noscript> blocks in your <head> that contain non-metadata elements (like <iframe>, <p>, <div>, <img>, etc.) and move them into the <body>. If the <noscript> block only needs metadata elements like <meta> or <style>, it can remain in the <head>.
Examples
Invalid: iframe inside noscript in head
The <iframe> is not valid metadata content, so it cannot appear inside <noscript> within the <head>.
<!DOCTYPE html>
<html lang="en">
<head>
<title>My webpage</title>
<noscript>
<iframe src="https://example.com/tracking"></iframe>
</noscript>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Fixed: noscript with iframe moved to body
Moving the <noscript> block into the <body> resolves the error, since flow content is allowed there.
<!DOCTYPE html>
<html lang="en">
<head>
<title>My webpage</title>
</head>
<body>
<noscript>
<iframe src="https://example.com/tracking"></iframe>
</noscript>
<h1>Welcome</h1>
</body>
</html>
Valid: metadata-only noscript in head
A <noscript> block that contains only metadata elements is perfectly valid inside the <head>.
<!DOCTYPE html>
<html lang="en">
<head>
<title>My webpage</title>
<noscript>
<meta http-equiv="refresh" content="0; url=https://example.com/nojs">
<style>
.js-only { display: none; }
</style>
</noscript>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
Fixed: splitting a mixed noscript between head and body
If you need both metadata and flow content in your <noscript> fallback, use two separate <noscript> blocks — one in the <head> for metadata, and one in the <body> for visible content.
<!DOCTYPE html>
<html lang="en">
<head>
<title>My webpage</title>
<noscript>
<style>
.js-only { display: none; }
</style>
</noscript>
</head>
<body>
<noscript>
<p>Please enable JavaScript to view this website.</p>
<iframe src="https://example.com/tracking"></iframe>
</noscript>
<h1>Welcome</h1>
</body>
</html>
The <noscript> element provides fallback content for users whose browsers don’t support scripting or have JavaScript disabled. When <noscript> appears inside <head>, the HTML specification restricts its contents to metadata elements only — specifically <link>, <style>, and <meta>. An <img> tag is flow content, not metadata content, so it is not permitted inside <head> regardless of whether it’s wrapped in <noscript>.
When <noscript> appears inside <body>, however, it can contain any flow content that would normally be valid at that position — including <img> elements. This is why the fix is simply to relocate the <noscript> block from the <head> to the <body>.
This issue is extremely common with third-party tracking pixels. Services like Facebook Pixel, LinkedIn Insight Tag, Google Tag Manager, and similar tools often instruct you to paste a <noscript> block containing a 1×1 tracking <img> directly after the opening <head> tag. While this works in browsers (they are forgiving with invalid HTML), it does not conform to the HTML specification and will trigger a validation error.
Beyond standards compliance, keeping your HTML valid ensures predictable behavior across all browsers and assistive technologies. Invalid markup can lead to unexpected DOM construction, which may cause subtle issues with CSS selectors, JavaScript DOM queries, or accessibility tools.
How to Fix It
- Locate the <noscript> block that contains the <img> element inside your <head>.
- Move that entire <noscript> block to somewhere inside the <body> — typically right after the opening <body> tag.
- Keep any associated <script> tag in the <head> if desired; only the <noscript> with the <img> needs to move.
Examples
❌ Invalid: <img> inside <noscript> in <head>
This is the pattern commonly provided by third-party tracking pixel instructions:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<script>
/* tracking script */
</script>
<noscript>
<img src="https://example.com/pixel?id=123" height="1" width="1" alt="">
</noscript>
</head>
<body>
<h1>Welcome</h1>
</body>
</html>
The validator reports “Bad start tag in img in noscript in head” because <img> is not valid metadata content.
✅ Valid: <noscript> with <img> moved to <body>
Move the <noscript> block into the <body> while leaving the <script> in the <head>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
<script>
/* tracking script */
</script>
</head>
<body>
<noscript>
<img src="https://example.com/pixel?id=123" height="1" width="1" alt="">
</noscript>
<h1>Welcome</h1>
</body>
</html>
The <img> is now inside <body> (via <noscript>), where flow content is permitted. The tracking pixel will still function correctly for users with JavaScript disabled.
✅ Valid: metadata-only <noscript> in <head>
If you only need metadata fallback content in the <head>, elements like <meta>, <link>, and <style> are allowed:
<head>
<title>My Page</title>
<noscript>
<style>
.js-only { display: none; }
</style>
</noscript>
</head>
This is valid because <style> is metadata content. Use this pattern when you need to apply fallback styles for non-JavaScript users, and reserve <noscript> blocks with <img> or other flow content for the <body>.
The HTML specification defines two distinct content models for the <noscript> element based on its location in the document. When <noscript> appears inside <head>, it inherits the restrictions of the <head> element itself — only metadata content is permitted. This means elements like <link>, <style>, and <meta> are allowed, but flow content like <p>, <div>, <h1>, or any other body-level element is not. When <noscript> appears inside <body>, it follows the more permissive rules of flow content and can contain paragraphs, headings, and other visible elements.
This distinction exists because the <head> section is strictly reserved for metadata that describes the document — it is never rendered as visible page content. Placing a <p> tag inside a <noscript> in <head> violates this principle. Browsers may handle this inconsistently: some might silently ignore the invalid content, while others might force the <head> to close prematurely and push the content into the <body>, causing unexpected layout issues and potentially disrupting other metadata elements that follow.
This is also an accessibility concern. Screen readers and other assistive technologies rely on a well-structured document where the <head> contains only metadata and all visible content lives in the <body>. Invalid nesting can confuse these tools and lead to content being skipped or misinterpreted.
How to Fix It
You have two main approaches:
-
Keep <noscript> in <head> but use only metadata elements. This is ideal when you need to load a fallback stylesheet or add a <meta> redirect for users without JavaScript.
-
Move the <noscript> block into <body> if you need to display visible text or other flow content to the user.
Examples
❌ Incorrect: Flow content inside <noscript> in <head>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example</title>
<noscript>
<p>JavaScript is disabled.</p>
</noscript>
</head>
<body>
</body>
</html>
The <p> element is flow content and is not allowed inside <noscript> when it sits within <head>.
✅ Correct: Metadata-only <noscript> in <head>
If your goal is to provide a fallback stylesheet or redirect when JavaScript is unavailable, use only metadata elements:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example</title>
<noscript>
<link rel="stylesheet" href="no-js-fallback.css">
</noscript>
</head>
<body>
</body>
</html>
You can also use <style> or <meta> inside the <noscript> in <head>:
<head>
<title>Example</title>
<noscript>
<style>
.js-only { display: none; }
.no-js-message { display: block; }
</style>
</noscript>
</head>
✅ Correct: Textual fallback in <noscript> inside <body>
When you need to show a visible message to users who have JavaScript disabled, place the <noscript> block in the <body>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example</title>
</head>
<body>
<noscript>
<p>This site requires JavaScript to function properly. Please enable JavaScript in your browser settings.</p>
</noscript>
</body>
</html>
✅ Correct: Combining both approaches
For a comprehensive fallback strategy, you can use <noscript> in both locations — metadata in <head> and visible content in <body>:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example</title>
<noscript>
<link rel="stylesheet" href="no-js-fallback.css">
</noscript>
</head>
<body>
<noscript>
<p>JavaScript is disabled. Some features may not be available.</p>
</noscript>
</body>
</html>
This gives you the best of both worlds: styling adjustments via the <head> fallback and a user-facing message via the <body> fallback.
The aria-rowspan attribute is used in ARIA-based grid and table structures to indicate how many rows a cell spans. It serves a similar purpose to the rowspan attribute on native HTML <td> and <th> elements, but is designed for custom widgets built with ARIA roles like gridcell, rowheader, and columnheader within grid or treegrid structures.
According to the WAI-ARIA specification, the value of aria-rowspan must be a positive integer — a whole number greater than zero. A value of "0" is invalid because it implies the cell spans no rows, which is semantically meaningless. Note that this differs from native HTML’s rowspan attribute, where "0" has a special meaning (span all remaining rows in the row group). The ARIA attribute does not support this behavior.
This matters primarily for accessibility. Screen readers and other assistive technologies rely on aria-rowspan to convey the structure of custom grids to users. An invalid value of "0" can confuse assistive technology, potentially causing it to misrepresent the grid layout or skip the cell entirely. Ensuring valid values helps users who depend on these tools navigate your content correctly.
To fix this issue, determine how many rows the cell actually spans and set aria-rowspan to that number. If the cell occupies a single row, use "1". If it spans multiple rows, use the appropriate count. If you don’t need row spanning at all, you can simply remove the aria-rowspan attribute entirely, since the default behavior is to span one row.
Examples
Incorrect: aria-rowspan set to zero
<div role="grid">
<div role="row">
<div role="gridcell" aria-rowspan="0">Name</div>
<div role="gridcell">Value</div>
</div>
</div>
The value "0" is not a positive integer, so the validator reports an error.
Correct: aria-rowspan set to a positive integer
If the cell spans a single row, use "1" (or remove the attribute, since one row is the default):
<div role="grid">
<div role="row">
<div role="gridcell" aria-rowspan="1">Name</div>
<div role="gridcell">Value</div>
</div>
</div>
Correct: aria-rowspan for a cell spanning multiple rows
If the cell genuinely spans two rows, set the value accordingly:
<div role="grid">
<div role="row">
<div role="gridcell" aria-rowspan="2">Category</div>
<div role="gridcell">Item A</div>
</div>
<div role="row">
<div role="gridcell">Item B</div>
</div>
</div>
Correct: removing the attribute when no spanning is needed
If the cell doesn’t span multiple rows, the simplest fix is to remove aria-rowspan altogether:
<div role="grid">
<div role="row">
<div role="gridcell">Name</div>
<div role="gridcell">Value</div>
</div>
</div>
When to use aria-rowspan vs. native HTML
If you’re building a data table, prefer native HTML <table>, <tr>, <td>, and <th> elements with the standard rowspan attribute. Native table semantics are automatically understood by browsers and assistive technologies without any ARIA attributes. Reserve aria-rowspan for custom interactive widgets (like spreadsheet-style grids or tree grids) where native table elements aren’t appropriate. The aria-rowspan value should always match the actual visual and structural layout of your grid to avoid confusing assistive technology users.
The step attribute specifies the granularity that an input’s value must adhere to. It controls the increment when a user clicks the up/down spinner buttons on a number input, moves a slider on a range input, or adjusts date and time inputs. The browser also uses this value during constraint validation to determine which values are considered valid based on the stepping base (typically the min value or the input’s default).
When the HTML specification defines the step attribute, it requires the value to be a valid floating-point number greater than zero. A value of "0" is explicitly invalid because a step of zero means no increment at all — it’s logically meaningless. You can’t step through values in increments of nothing. The W3C validator flags this as an error because "0" fails the requirement of being a positive number.
Why this matters
- Standards compliance: The WHATWG HTML living standard explicitly requires step to parse as a number greater than zero. A value of "0" violates this rule.
- Browser behavior: While browsers may not crash on step="0", the behavior becomes unpredictable. Spinner buttons may stop working, and form validation may not function as expected.
- Accessibility: Assistive technologies rely on correct step values to communicate valid input ranges to users. An invalid step can lead to a confusing experience.
How to fix it
Choose the appropriate fix depending on your intent:
- If you want specific increments (e.g., whole numbers, cents, tenths), set step to the desired interval like "1", "0.01", or "0.1".
- If you want to allow any value with no stepping constraint, use the special keyword step="any". This tells the browser that no stepping is implied and any floating-point value is acceptable.
- If you don’t need the attribute at all, simply remove it. Each input type has a default step value (e.g., 1 for type="number").
Examples
❌ Invalid: step set to zero
<label for="price">Price:</label>
<input id="price" name="price" type="number" step="0" min="0">
This triggers the validation error because "0" is not a valid positive floating-point number.
✅ Fixed: using a specific step value
If you want the input to accept values in increments of one cent:
<label for="price">Price:</label>
<input id="price" name="price" type="number" step="0.01" min="0">
Valid values would include 0, 0.01, 0.02, 1.50, 99.99, and so on.
✅ Fixed: using step="any" for unrestricted precision
If you want to allow any numeric value without stepping constraints:
<label for="price">Price:</label>
<input id="price" name="price" type="number" step="any" min="0">
This permits any floating-point value, such as 3.14159 or 0.007.
✅ Fixed: using a whole number step with a non-zero minimum
<label for="quantity">Quantity:</label>
<input id="quantity" name="quantity" type="number" step="2" min="1.3">
With step="2" and min="1.3", valid values include 1.3, 3.3, 5.3, 7.3, and so on. The stepping base is 1.3, and each valid value is an even multiple of 2 away from it.
✅ Fixed: removing the attribute entirely
If the default step behavior is sufficient, simply omit the attribute:
<label for="amount">Amount:</label>
<input id="amount" name="amount" type="number" min="0">
The default step for type="number" is 1, so valid values are whole numbers (0, 1, 2, etc.).
The aria-setsize attribute tells assistive technologies how many items exist in a set of related elements (such as list items or tree items). It is particularly useful when not all items in a set are present in the DOM — for example, in virtualized lists, paginated results, or lazy-loaded content. According to the WAI-ARIA 1.2 specification:
Authors MUST set the value of aria-setsize to an integer equal to the number of items in the set. If the total number of items is unknown, authors SHOULD set the value of aria-setsize to -1.
This means -1 is a specifically recommended value for cases where the total count of items is indeterminate. The W3C validator’s pattern matching expects only non-negative digits, so it rejects the leading - character. This is a known validator bug and does not reflect an actual problem with your HTML.
Why -1 matters for accessibility
When building interfaces with dynamic or partially loaded content, screen readers need to communicate the size of a set to users. If you have a list of search results but don’t know the total count, setting aria-setsize="-1" tells assistive technologies that the set size is unknown. Without this, a screen reader might announce incorrect or misleading information about how many items exist.
What you should do
Do not change your markup to work around this validator error. The value -1 is correct and serves an important accessibility purpose. Removing it or replacing it with an arbitrary positive number would degrade the experience for users of assistive technologies.
Examples
Valid usage that triggers the false positive
This markup is correct but will be flagged by the validator:
<ul>
<li role="option" aria-setsize="-1" aria-posinset="1">Result A</li>
<li role="option" aria-setsize="-1" aria-posinset="2">Result B</li>
<li role="option" aria-setsize="-1" aria-posinset="3">Result C</li>
</ul>
Here, the total number of results is unknown (perhaps they are loaded on demand), so aria-setsize="-1" correctly signals this to assistive technologies.
Known set size (no validator error)
When the total number of items is known, use the actual count. This will not trigger the validator error:
<ul>
<li role="option" aria-setsize="5" aria-posinset="1">Item 1</li>
<li role="option" aria-setsize="5" aria-posinset="2">Item 2</li>
<li role="option" aria-setsize="5" aria-posinset="3">Item 3</li>
</ul>
When aria-setsize is not needed
If all items in the set are present in the DOM, you don’t need aria-setsize at all — the browser can compute the set size automatically:
<ul role="listbox">
<li role="option">Apple</li>
<li role="option">Banana</li>
<li role="option">Cherry</li>
</ul>
In summary, if you see this validator error and you’re intentionally using aria-setsize="-1" because the total item count is unknown, your code is correct. You can safely ignore this particular warning.
The HTML specification defines the height attribute on <img> as a “valid non-negative integer,” which means it must consist only of digits — no units, no percentage signs. When you write height="100%", the validator expects a digit character but encounters %, producing this error. The same rule applies to the width attribute.
This matters for several reasons. First, browsers use the width and height attributes to reserve the correct amount of space for an image before it loads, which prevents layout shifts (a key performance metric known as Cumulative Layout Shift). When the value contains invalid characters like %, browsers may ignore the attribute entirely or interpret it unpredictably, undermining this layout reservation. Second, invalid HTML can cause inconsistent rendering across different browsers and assistive technologies. Third, the percentage-based sizing you likely intended simply isn’t supported through HTML attributes — it requires CSS.
The fix depends on what you’re trying to achieve:
- Fixed dimensions: Replace the percentage with a plain integer representing the image’s intrinsic or desired pixel size (e.g., height="300").
- Responsive or percentage-based sizing: Remove the height attribute (or set it to the image’s intrinsic pixel dimensions) and use CSS to control how the image scales within its container.
It’s a good practice to always include both width and height attributes with the image’s actual intrinsic dimensions, then use CSS to override the display size. This gives browsers the aspect ratio information they need to reserve space while still allowing flexible layouts.
Examples
Incorrect: percentage value in the height attribute
This triggers the validation error because % is not a valid digit:
<img src="photo.jpg" width="100%" height="100%" alt="A landscape photo">
Fixed: using integer pixel values
Specify the image’s intrinsic dimensions as plain numbers:
<img src="photo.jpg" width="800" height="600" alt="A landscape photo">
Fixed: combining HTML attributes with CSS for responsive sizing
Set the intrinsic dimensions in HTML for layout stability, then use CSS to make the image responsive:
<style>
.responsive-img {
width: 100%;
height: auto;
}
</style>
<img
src="photo.jpg"
width="800"
height="600"
class="responsive-img"
alt="A landscape photo">
With this approach, the browser knows the image’s aspect ratio (800×600) and reserves the appropriate space, while CSS ensures the image scales fluidly to fill its container. The height: auto rule maintains the correct aspect ratio as the width changes.
Fixed: filling a container with CSS only
If you don’t know the image’s intrinsic dimensions and simply want it to fill a container, you can omit the HTML attributes and rely entirely on CSS:
<style>
.image-container {
width: 300px;
height: 200px;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
<div class="image-container">
<img src="photo.jpg" alt="A landscape photo">
</div>
The object-fit: cover property ensures the image fills the container without distortion, cropping as needed. Note that omitting width and height attributes means the browser cannot reserve space before the image loads, so this approach may cause layout shifts. Where possible, prefer including the intrinsic dimensions as shown in the previous example.
The alert ARIA role is used to communicate important, typically time-sensitive messages to the user. When an element has role="alert", assistive technologies like screen readers will immediately announce its content to the user, interrupting whatever they are currently doing. This makes it ideal for error messages, warnings, or status updates that require immediate attention.
However, not every HTML element can accept every ARIA role. The WHATWG HTML specification and WAI-ARIA in HTML define rules about which roles are allowed on which elements. The <ul> element has an implicit role of list, and the alert role is not among the roles permitted on <ul>. This restriction exists because overriding the semantic meaning of a list element with an alert role creates a conflict — assistive technologies would no longer convey the list structure to users, and the element’s children (<li> elements) would lose their meaningful context as list items.
This matters for accessibility and standards compliance. If a screen reader encounters a <ul> with role="alert", the behavior becomes unpredictable. Some screen readers might announce it as an alert but fail to convey the list structure, while others might ignore the role entirely. Users who rely on assistive technology could miss either the alert or the list semantics, both of which may be important.
The fix depends on your intent. If you need to alert the user about content that happens to include a list, wrap the <ul> in a container element (like a <div>) and apply role="alert" to that container. If the content doesn’t need to be a list, replace the <ul> with a more appropriate element like <div> or <p>.
Examples
❌ Invalid: role="alert" directly on a <ul>
<ul role="alert">
<li>Your password must be at least 8 characters.</li>
<li>Your password must contain a number.</li>
</ul>
This triggers the validation error because alert is not a valid role for the <ul> element.
✅ Fixed: Wrapping the list in a <div> with role="alert"
<div role="alert">
<ul>
<li>Your password must be at least 8 characters.</li>
<li>Your password must contain a number.</li>
</ul>
</div>
Here, the <div> carries the role="alert", so assistive technologies will announce the content immediately. The <ul> retains its native list semantics, and the <li> items are properly conveyed as list items.
✅ Fixed: Using a non-list element when list structure isn’t needed
<div role="alert">
<p>Your session will expire in 2 minutes.</p>
</div>
If your alert content is a simple message rather than a list of items, use a more appropriate element like <p> or <div>.
✅ Fixed: Using aria-live as an alternative for dynamic updates
<div aria-live="assertive" role="alert">
<ul>
<li>Error: Email address is required.</li>
<li>Error: Name field cannot be empty.</li>
</ul>
</div>
The aria-live="assertive" attribute on the wrapper ensures that when the content is dynamically updated, assistive technologies announce the changes immediately. Combined with role="alert" on the wrapper (not the list), this provides robust accessible notifications while preserving list semantics.
Key points to remember
- The role="alert" attribute cannot be placed on <ul>, <ol>, or <li> elements.
- Always apply role="alert" to a generic container element like <div> or <span>.
- If your alert content includes a list, nest the list inside the alert container rather than making the list itself the alert.
- The alert role implicitly sets aria-live="assertive" and aria-atomic="true", so you don’t need to add those separately when using role="alert".
The sandbox attribute applies a strict set of restrictions to content loaded inside an <iframe>. By default, sandboxed iframes cannot run scripts, submit forms, open popups, or access storage. You can selectively lift specific restrictions by adding recognized keywords like allow-scripts, allow-forms, allow-popups, allow-same-origin, and others defined in the WHATWG HTML standard.
The keyword allow-storage-access-by-user-activation was proposed as a way to let sandboxed iframes request access to first-party storage (such as cookies) after a user gesture. However, this keyword was never adopted into the HTML specification. The functionality it aimed to provide is now handled by the Storage Access API, which uses document.requestStorageAccess() and document.hasStorageAccess() in JavaScript. Because the keyword was never standardized, the W3C validator correctly flags it as invalid.
Why this matters
- Standards compliance: Using non-standard keywords means your HTML doesn’t conform to the specification, which the validator will flag as an error.
- Browser inconsistency: Since this keyword was experimental and never standardized, browser support is unreliable. Some browsers may silently ignore it, while others may have briefly supported it before removing it.
- False sense of security: Including an unrecognized sandbox keyword doesn’t actually enable the behavior you expect. The iframe won’t gain storage access just because this keyword is present—the browser simply ignores unknown tokens.
How to fix it
- Remove the invalid keyword from the sandbox attribute.
- Keep any other valid sandbox keywords that your iframe needs.
- Use the Storage Access API in JavaScript within the iframe if you need cross-site storage access. The embedded page must call document.requestStorageAccess() in response to a user gesture, and the sandbox attribute must include allow-scripts and allow-same-origin for this API to work.
Examples
❌ Invalid: using a non-standard sandbox keyword
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin allow-storage-access-by-user-activation">
</iframe>
✅ Valid: removing the non-standard keyword
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin">
</iframe>
The embedded page at https://example.com/widget can then use the Storage Access API in JavaScript:
document.querySelector('#login-button').addEventListener('click', async () => {
const hasAccess = await document.hasStorageAccess();
if (!hasAccess) {
await document.requestStorageAccess();
}
// Storage (cookies, etc.) is now accessible
});
✅ Valid: sandbox with other standard keywords
If your iframe doesn’t need storage access at all, simply use the standard keywords you require:
<iframe
src="https://example.com/form"
sandbox="allow-scripts allow-forms allow-popups">
</iframe>
Note that for document.requestStorageAccess() to work inside a sandboxed iframe, you must include both allow-scripts (so JavaScript can run) and allow-same-origin (so the iframe retains its origin). Without these, the Storage Access API calls will fail.
The aria-expanded attribute communicates to assistive technologies whether a related grouping element (such as a dropdown menu, accordion panel, or collapsible section) is currently expanded or collapsed. It accepts only three valid values:
- "true" — the controlled element is expanded and visible.
- "false" — the controlled element is collapsed and hidden.
- "undefined" — the element has no expandable relationship (this is also the implicit default when the attribute is omitted entirely).
This validation error typically occurs when the attribute is accidentally set to a non-boolean value. A common mistake is writing aria-expanded="aria-expanded", which mimics the old HTML4 pattern for boolean attributes like checked="checked". However, aria-expanded is not a standard HTML boolean attribute — it is an ARIA state attribute that requires an explicit string value of "true" or "false".
Setting an invalid value means assistive technologies like screen readers cannot correctly interpret the state of the control. A screen reader user may not know whether a menu is open or closed, leading to a confusing and inaccessible experience. Browsers may also handle the invalid value unpredictably, potentially treating it as truthy or ignoring it altogether.
How to fix it
- Identify the element with the invalid aria-expanded value.
- Replace the value with "true" if the associated content is currently expanded, or "false" if it is collapsed.
- If the button has no expand/collapse relationship at all, remove the aria-expanded attribute entirely.
- Ensure that JavaScript toggling logic updates the attribute to "true" or "false" — never to any other string.
Examples
❌ Invalid: attribute set to a non-boolean string
<button aria-expanded="aria-expanded" aria-controls="menu">
Toggle Menu
</button>
<ul id="menu">
<li>Option 1</li>
<li>Option 2</li>
</ul>
The value "aria-expanded" is not a recognized value and triggers the validation error.
✅ Fixed: attribute set to "false" (collapsed state)
<button aria-expanded="false" aria-controls="menu">
Toggle Menu
</button>
<ul id="menu" hidden>
<li>Option 1</li>
<li>Option 2</li>
</ul>
✅ Fixed: attribute set to "true" (expanded state)
<button aria-expanded="true" aria-controls="menu">
Toggle Menu
</button>
<ul id="menu">
<li>Option 1</li>
<li>Option 2</li>
</ul>
❌ Invalid: other common incorrect values
<!-- Using "yes" instead of "true" -->
<button aria-expanded="yes">Details</button>
<!-- Using "1" instead of "true" -->
<button aria-expanded="1">Details</button>
<!-- Empty value -->
<button aria-expanded="">Details</button>
All of these are invalid. The only accepted values are "true", "false", and "undefined".
✅ Toggling with JavaScript
When toggling aria-expanded dynamically, make sure the value is always set to the correct string:
<button aria-expanded="false" aria-controls="panel" onclick="togglePanel(this)">
Show details
</button>
<div id="panel" hidden>
<p>Additional details here.</p>
</div>
<script>
function togglePanel(button) {
const expanded = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!expanded));
const panel = document.getElementById(button.getAttribute("aria-controls"));
panel.hidden = expanded;
}
</script>
This ensures the attribute always toggles between "true" and "false", keeping the markup valid and the experience accessible for all users.
The W3C HTML validator enforces rules about which ARIA roles can be applied to specific HTML elements. The <section> element carries implicit semantics — it maps to the ARIA region role when it has an accessible name (e.g., via aria-label or aria-labelledby). While article is indeed a valid ARIA role defined in the WAI-ARIA specification, the HTML specification restricts which roles can override a <section> element’s native semantics. The allowed roles for <section> include alert, alertdialog, application, contentinfo, dialog, document, feed, log, main, marquee, navigation, none, note, presentation, search, status, tabpanel, and region — but not article.
This restriction exists because HTML already provides the <article> element, which carries the implicit article ARIA role natively. Using role="article" on a <section> creates a confusing mismatch: the element’s tag name suggests one semantic meaning while the role attribute declares another. This can confuse assistive technologies like screen readers, which may announce the element inconsistently depending on whether they prioritize the tag name or the explicit role.
The best fix depends on your intent:
- If the content is self-contained and independently meaningful (like a blog post, comment, or news story), replace the <section> with an <article> element. The <article> element already has the implicit article role, so no role attribute is needed.
- If the content is a thematic grouping within a page, keep the <section> element and remove the role attribute. Give it a heading or an aria-label so it functions as a meaningful landmark.
- If you specifically need the article role on a non-semantic element, use a <div> with role="article" instead, since <div> has no implicit role and allows any ARIA role to be applied.
Examples
Incorrect: role="article" on a <section>
This triggers the validation error because article is not a permitted role for <section>.
<section role="article">
<h2>Breaking news</h2>
<p>Details about the event.</p>
</section>
Correct: use <article> for self-contained content
The <article> element has the implicit article role, making the explicit role attribute unnecessary.
<article>
<h2>Breaking news</h2>
<p>Details about the event.</p>
</article>
Correct: use <section> without a conflicting role
If the content is a thematic grouping rather than a standalone piece, keep <section> and drop the role attribute. Adding an accessible name via aria-labelledby makes it a region landmark.
<section aria-labelledby="news-heading">
<h2 id="news-heading">Latest news</h2>
<p>Details about the event.</p>
</section>
Correct: use a <div> when you need an explicit article role
In rare cases where you cannot use the <article> element but need the article role, a <div> accepts any valid ARIA role.
<div role="article">
<h2>Breaking news</h2>
<p>Details about the event.</p>
</div>
Correct: nest <article> inside <section> for grouped articles
If you need both a thematic grouping and individual self-contained items, nest <article> elements inside a <section>.
<section aria-labelledby="stories-heading">
<h2 id="stories-heading">Top stories</h2>
<article>
<h3>First story</h3>
<p>Story content.</p>
</article>
<article>
<h3>Second story</h3>
<p>Story content.</p>
</article>
</section>
As a general rule, prefer native HTML elements over ARIA roles whenever possible. The <article> element communicates the article role more reliably than any ARIA override, and it works consistently across all browsers and assistive technologies without additional attributes.
The height attribute on an img element contains the string auto" instead of a valid non-negative integer, likely due to a typo with an extra quote.
The height attribute on img elements only accepts non-negative integers representing the image’s height in CSS pixels. Values like auto, percentages, or any non-numeric strings are not valid. The extra trailing quote (") in auto" suggests a copy-paste error or a quoting mistake in your markup.
If you want the image height to adjust automatically, simply omit the height attribute altogether, or set height: auto in CSS instead. It’s still recommended to include both width and height attributes with actual pixel values to help the browser reserve the correct space and prevent layout shifts (CLS).
Invalid Example
<img src="photo.jpg" alt="A photo" width="600" height="auto">
Fixed Example
Using CSS for automatic height while keeping the HTML attribute for layout stability:
<img src="photo.jpg" alt="A photo" width="600" height="400" style="height: auto;">
Or simply omit the height attribute if you don’t know the intrinsic dimensions:
<img src="photo.jpg" alt="A photo" width="600">
The HTML specification defines the height attribute on media elements like <video> as accepting only a valid non-negative integer. This means the attribute value must consist solely of digits (e.g., "360"), with no units, keywords, or other characters. When you write height="auto", the validator expects to find a digit as the first character but encounters the letter "a", which produces the error.
The value "auto" is a valid concept in CSS, where height: auto tells the browser to calculate the element’s height automatically based on its intrinsic aspect ratio or content. However, HTML attributes and CSS properties follow different rules. The height HTML attribute is a simple pixel dimension hint — it doesn’t understand CSS keywords. Mixing CSS values into HTML attributes is a common mistake, and while browsers may silently ignore the invalid value, it leads to unpredictable behavior: the video may render without any height hint, potentially causing layout shifts as the browser discovers the video’s actual dimensions during loading.
Providing a valid height attribute matters for layout stability. When the browser knows the video’s dimensions before the media loads, it can reserve the correct amount of space in the page layout, preventing content from jumping around. This improves the user experience and contributes to better Core Web Vitals scores (specifically Cumulative Layout Shift). It also ensures your HTML is standards-compliant and accessible to assistive technologies that may rely on well-formed markup.
How to Fix
You have two approaches:
- Use a numeric value — Replace "auto" with an integer that represents the video’s height in pixels.
- Use CSS instead — Remove the height attribute from the HTML and apply height: auto (or any other value) via CSS. This is ideal when you want the video to scale responsively.
If you want the video to maintain its aspect ratio while scaling, the CSS approach is generally preferred for responsive designs. You can combine a width attribute (or CSS width) with CSS height: auto to let the browser calculate the correct height from the video’s intrinsic aspect ratio.
Examples
❌ Invalid: Using “auto” in the HTML attribute
<video width="640" height="auto" controls>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
This triggers the validation error because "auto" is not a non-negative integer.
✅ Fixed: Using a numeric height value
<video width="640" height="360" controls>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
The height attribute is now a valid integer. The browser reserves a 640×360 pixel area for the video before it loads.
✅ Fixed: Using CSS for responsive sizing
<video width="640" controls style="height: auto; max-width: 100%;">
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
Here the height HTML attribute is removed entirely. CSS height: auto ensures the video scales proportionally, and max-width: 100% prevents it from overflowing its container. This is a common pattern for responsive video.
✅ Fixed: Using both attributes with CSS override
<video width="640" height="360" controls style="width: 100%; height: auto;">
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
This approach provides the best of both worlds: the width and height HTML attributes give the browser an aspect ratio hint (preventing layout shifts), while the CSS makes the video responsive. Modern browsers use the attribute values to calculate the correct aspect ratio even when CSS overrides the actual rendered size.
The HTML specification defines the width and height attributes on <img> as accepting only valid non-negative integers — a sequence of one or more digits (0–9) with no letters, units, or symbols. These attributes tell the browser the intrinsic dimensions of the image in pixels, which helps it allocate the correct space in the layout before the image loads, preventing content layout shift (CLS).
When you set height="auto" or width="50%", the validator expects a digit as the first character but instead encounters a letter or symbol, producing the error: “Bad value ‘auto’ for attribute ‘height’ on element ‘img’: Expected a digit but saw ‘a’ instead.”
This matters for several reasons:
- Standards compliance: Browsers may silently ignore invalid attribute values, meaning your intended sizing won’t take effect and you’ll get default behavior without any visible warning to users.
- Layout stability: Valid width and height attributes allow the browser to calculate the image’s aspect ratio before it loads, reserving the correct amount of space and preventing layout shifts. Invalid values defeat this mechanism.
- Predictability: Relying on browser error recovery for invalid markup leads to inconsistent behavior across different browsers and versions.
To fix this, you have two options:
- Use plain integers in the width and height attributes to specify pixel dimensions (e.g., width="600" height="400").
- Use CSS for any non-pixel or dynamic sizing like auto, percentages, max-width, viewport units, etc.
A best practice is to set the width and height attributes to the image’s actual intrinsic pixel dimensions (to preserve aspect ratio and prevent layout shift), then use CSS to control the rendered size responsively.
Examples
Invalid: using “auto” or units in attributes
<!-- "auto" is not a valid integer -->
<img src="photo.jpg" alt="A cat sitting on a windowsill" height="auto" width="auto">
<!-- Percentage is not a valid integer -->
<img src="banner.jpg" alt="Site banner" width="100%">
<!-- Units like "px" are not allowed -->
<img src="icon.png" alt="Settings icon" width="32px" height="32px">
Valid: using plain integers in attributes
<!-- Correct: plain integers representing pixels -->
<img src="photo.jpg" alt="A cat sitting on a windowsill" width="800" height="600">
<img src="icon.png" alt="Settings icon" width="32" height="32">
Valid: using CSS for responsive or dynamic sizing
When you need behavior like auto, percentages, or max-width, use CSS instead:
<!-- Use attributes for intrinsic size, CSS for responsive behavior -->
<img
src="photo.jpg"
alt="A cat sitting on a windowsill"
width="800"
height="600"
style="max-width: 100%; height: auto;">
This approach gives you the best of both worlds: the browser knows the image’s aspect ratio from the attributes (preventing layout shift), while CSS ensures it scales responsively within its container.
Valid: using a CSS class for reusability
<style>
.responsive-img {
max-width: 100%;
height: auto;
}
</style>
<img
src="photo.jpg"
alt="A cat sitting on a windowsill"
width="800"
height="600"
class="responsive-img">
The sizes attribute works together with the srcset attribute to help the browser choose the most appropriate image source for the current viewport and layout. Its value must follow a specific syntax: a comma-separated list where each entry is an optional media condition followed by a CSS length value. The final entry acts as a default and should be a length value without a media condition.
The value "auto" is not recognized as a valid CSS length or source size descriptor by the current HTML specification. While there is a newer sizes="auto" feature being developed for use specifically with loading="lazy" images, it is not yet universally part of the validated standard, and the W3C validator flags it as invalid. When the validator encounters auto, it expects a number (like 100vw or 50px) or a minus sign, but instead finds the letter “a”, producing this error.
Why This Matters
- Standards compliance: Using invalid attribute values means your HTML doesn’t conform to the specification, which could lead to unpredictable behavior across browsers.
- Responsive image loading: When sizes contains an invalid value, browsers may fall back to the default value of 100vw, which can cause them to download unnecessarily large images, hurting performance.
- Accessibility and tooling: Invalid HTML can cause issues with assistive technologies and automated tools that rely on well-formed markup.
How to Fix It
You have several options depending on your use case:
- Specify explicit sizes — Provide a valid source size list that describes how wide the image will be displayed at various viewport widths.
- Use a simple default — Set sizes to a single CSS length like "100vw" if the image always spans the full viewport width.
- Remove sizes entirely — If you don’t use srcset with width descriptors, the sizes attribute is unnecessary and can be removed.
- Use sizes="auto" with loading="lazy" — If you intentionally want the browser to determine sizes automatically for lazy-loaded images, be aware this is a newer feature that the validator may not yet support. You can suppress this specific warning if you’ve confirmed browser support meets your needs.
Examples
❌ Invalid: Using auto as the sizes value
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="auto"
alt="A scenic mountain view">
This triggers the error because auto is not a valid CSS length or source size descriptor.
✅ Fixed: Using a responsive source size list
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"
alt="A scenic mountain view">
This tells the browser: use 100% of the viewport width on screens up to 600px, 50% on screens up to 1000px, and 33% on larger screens. The browser then picks the best image from srcset.
✅ Fixed: Using a simple default size
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="100vw"
alt="A scenic mountain view">
If the image always fills the full viewport width, 100vw is a straightforward valid value.
✅ Fixed: Removing sizes when srcset is not used
<img src="photo.jpg" alt="A scenic mountain view">
If you’re not using srcset with width descriptors, the sizes attribute serves no purpose and can be safely removed. Without it, the browser defaults to 100vw when interpreting any srcset width descriptors.
✅ Fixed: Using a fixed pixel width
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="400px"
alt="A scenic mountain view">
If the image is always displayed at a fixed width regardless of viewport size, you can specify that width directly as a CSS length value.
Ready to validate your sites?
Start your free trial today.