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 srcset attribute lets browsers intelligently choose which image to load based on the viewport size and device pixel ratio. Each entry in a srcset consists of a URL followed by either a width descriptor (like 300w) or a pixel density descriptor (like 2x). When using width descriptors, the value represents the intrinsic pixel width of the image file — that is, the actual width of the image as stored on disk.
A width descriptor of 0w violates the HTML specification, which requires width descriptors to be integers greater than zero. A zero-width image cannot meaningfully participate in the browser's source selection process. The browser uses these width values in combination with the sizes attribute to calculate which image best fits the current layout — a value of zero would break this calculation entirely.
This issue commonly occurs when:
- Image dimensions are dynamically generated and a fallback of
0is used for missing data. - A placeholder or empty state is accidentally included in the
srcset. - A CMS or build tool outputs a
0wdescriptor for images whose dimensions weren't computed.
Why it matters
- Standards compliance: The HTML specification explicitly requires width descriptors to be positive integers. Validators will flag
0was an error. - Browser behavior: While browsers may silently ignore the invalid entry, you can't rely on consistent handling across all browsers and versions. The image selection algorithm may behave unpredictably.
- Performance: A well-formed
srcsetis key to responsive image loading. Invalid descriptors can prevent browsers from selecting the optimal image, leading to unnecessarily large downloads or poor image quality.
How to fix it
- Open the image file associated with the
0wdescriptor and check its actual pixel width using an image editor or the command line. - Replace
0wwith the correct width (e.g.,150wfor a 150-pixel-wide image). - If the image is truly zero-width or a placeholder, remove that entry from the
srcsetentirely. - Ensure every remaining entry has a unique, positive width descriptor.
Examples
❌ Invalid: width descriptor of 0w
<picture>
<source
srcset="/images/icon_placeholder.png 0w,
/images/icon_large.png 600w"
media="(max-width: 600px)">
<img src="/images/icon_fallback.png" alt="App logo">
</picture>
The 0w descriptor triggers the validation error because zero is not a valid width.
✅ Fixed: all width descriptors are positive
<picture>
<source
srcset="/images/icon_small.png 300w,
/images/icon_large.png 600w"
media="(max-width: 600px)">
<img src="/images/icon_fallback.png" alt="App logo">
</picture>
Each entry now has a meaningful width descriptor (300w and 600w) that reflects the actual pixel width of the corresponding image.
❌ Invalid: 0w on an <img> element
<img
srcset="/images/hero_tiny.jpg 0w,
/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w"
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
✅ Fixed: placeholder entry removed or corrected
If the tiny image is 400 pixels wide, use 400w:
<img
srcset="/images/hero_tiny.jpg 400w,
/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w"
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
Alternatively, if the image doesn't belong in the set at all, simply remove it:
<img
srcset="/images/hero_medium.jpg 800w,
/images/hero_large.jpg 1200w"
sizes="100vw"
src="/images/hero_medium.jpg"
alt="Mountain landscape">
When using a build tool or CMS that generates srcset values dynamically, add a check to filter out any entries where the computed width is zero or missing before rendering the attribute. This prevents the invalid markup from reaching production.
The <source> element is used inside <picture>, <audio>, or <video> elements to specify alternative media resources. When used inside a <picture> element, the srcset attribute is required and must contain one or more comma-separated image candidate strings. Each image candidate string consists of a URL and an optional descriptor — either a width descriptor like 400w or a pixel density descriptor like 2x.
This validation error typically occurs when:
- The
srcsetattribute is present but empty (srcset=""). - The attribute value contains only whitespace.
- The value is malformed or contains syntax errors (e.g., missing URLs, invalid descriptors).
- A dynamic templating system or CMS outputs the attribute with no value.
Why this matters
Browsers rely on the srcset attribute to select the most appropriate image to display based on the user's device capabilities, viewport size, and network conditions. An empty or invalid srcset means the browser cannot perform this selection, potentially resulting in no image being displayed at all. This degrades the user experience, harms accessibility (screen readers and assistive technologies may encounter unexpected behavior), and violates the HTML specification as defined by the WHATWG living standard.
How to fix it
- Provide at least one valid image URL in the
srcsetattribute. - Optionally add descriptors — use width descriptors (
w) when combined with thesizesattribute, or pixel density descriptors (x) for fixed-size images. - If you have no image to provide, remove the
<source>element entirely rather than leavingsrcsetempty. - Check dynamic output — if a CMS or templating engine generates the
srcsetvalue, add a conditional check to omit the<source>element when no images are available.
Examples
❌ Empty srcset attribute
<picture>
<source srcset="" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This triggers the error because srcset is present but contains no image candidate strings.
❌ Invalid descriptor syntax
<picture>
<source srcset="photo.webp 400" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This is invalid because 400 is not a recognized descriptor — it must be 400w or a density descriptor like 2x.
✅ Single image candidate
<picture>
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
A single URL without a descriptor is valid and serves as the default 1x candidate.
✅ Multiple candidates with width descriptors
<picture>
<source
srcset="photo-small.webp 400w, photo-medium.webp 800w, photo-large.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1000px) 800px, 1200px"
type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This provides three image candidates with width descriptors, allowing the browser to choose the best match based on the viewport and display density.
✅ Multiple candidates with pixel density descriptors
<picture>
<source srcset="photo.webp 1x, photo-2x.webp 2x" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
Pixel density descriptors tell the browser which image to use based on the device's pixel ratio — 1x for standard displays and 2x for high-DPI (Retina) screens.
✅ Removing the source element when no image is available
If your application dynamically generates the srcset value and sometimes has no image to provide, omit the <source> element entirely:
<picture>
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
This is valid because the <img> element inside <picture> serves as the required fallback and can stand alone.
The srcset attribute supports two types of descriptors: width descriptors (e.g., 480w) and pixel density descriptors (e.g., 2x). These two types cannot be mixed, and the sizes attribute is specifically designed to work with width descriptors. The sizes attribute tells the browser how wide the image will be displayed at various viewport sizes, so the browser can then pick the best image from srcset based on the widths you've provided. If any candidate in srcset lacks a width descriptor — or uses a density descriptor instead — the browser can't perform this calculation, and the HTML is invalid.
This matters for several reasons. First, browsers rely on the combination of sizes and width descriptors to make intelligent decisions about which image to download before the layout is computed. An invalid srcset can lead to the browser ignoring the entire attribute or selecting a suboptimal image, wasting bandwidth or displaying a blurry result. Second, standards compliance ensures consistent behavior across all browsers and devices.
A common mistake is specifying sizes while using density descriptors (1x, 2x) or providing bare URLs without any descriptor in srcset. If you want to use density descriptors, simply remove the sizes attribute. If you want responsive image selection based on viewport width, use width descriptors for every candidate.
Examples
Incorrect: Using density descriptors with sizes
<picture>
<source
srcset="image-small.jpg 1x, image-large.jpg 2x"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
This triggers the error because 1x and 2x are density descriptors, but the sizes attribute requires width descriptors.
Incorrect: Missing descriptor on one candidate
<picture>
<source
srcset="image-small.jpg, image-large.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Here, image-small.jpg has no descriptor at all. When sizes is present, every candidate must have a width descriptor.
Correct: All candidates use width descriptors with sizes
<picture>
<source
srcset="image-small.jpg 400w, image-large.jpg 800w"
sizes="(max-width: 600px) 100vw, 50vw">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Each image candidate now specifies a width descriptor (400w, 800w), which matches the requirement imposed by the sizes attribute.
Correct: Using density descriptors without sizes
If you only need density-based selection (e.g., for retina displays) and don't need viewport-based sizing, remove the sizes attribute entirely:
<picture>
<source srcset="image-small.jpg 1x, image-large.jpg 2x">
<img src="image-small.jpg" alt="A landscape photo">
</picture>
Correct: Using srcset with width descriptors on <img>
The same rules apply when using srcset directly on an <img> element:
<img
srcset="photo-320.jpg 320w, photo-640.jpg 640w, photo-1024.jpg 1024w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 33vw"
src="photo-640.jpg"
alt="A mountain landscape">
Every candidate in srcset includes a width descriptor, making this fully valid alongside the sizes attribute. The src attribute serves as the fallback for browsers that don't support srcset.
The target attribute specifies where to display the linked resource. The HTML specification defines a set of reserved keywords that all begin with an underscore: _blank, _self, _parent, and _top. Any other value starting with an underscore is considered invalid because the underscore prefix is reserved for current and future keywords defined by the specification.
This matters for several reasons. First, browsers may handle unrecognized underscore-prefixed values inconsistently — some might treat them like _blank, while others might ignore them entirely or treat them as named browsing contexts. This leads to unpredictable behavior across different browsers. Second, using reserved but undefined keywords signals a likely typo or misunderstanding of the attribute, which could cause navigation to behave differently than intended. Standards compliance ensures your links work reliably for all users.
The valid keywords and their meanings are:
_self— Opens the link in the current browsing context (the default behavior)._blank— Opens the link in a new, unnamed browsing context (typically a new tab)._parent— Opens the link in the parent browsing context, or_selfif there is no parent._top— Opens the link in the topmost browsing context, or_selfif there is no ancestor.
If you need to target a specific named frame or window, simply use a name without a leading underscore. Any string that doesn't start with _ is treated as a valid named browsing context.
Examples
Incorrect: Invalid reserved keyword
These examples use underscore-prefixed values that are not recognized keywords:
<!-- Typo: "_blanks" is not a valid keyword -->
<a href="https://example.com" target="_blanks">Example</a>
<!-- "_new" is not a valid keyword -->
<a href="https://example.com" target="_new">Open in new tab</a>
<!-- "_tab" is not a valid keyword -->
<a href="https://example.com" target="_tab">Open link</a>
Correct: Using a valid keyword
<!-- Use "_blank" to open in a new tab -->
<a href="https://example.com" target="_blank">Example</a>
<!-- Use "_self" to open in the same tab (also the default) -->
<a href="https://example.com" target="_self">Example</a>
<!-- Use "_parent" to open in the parent frame -->
<a href="https://example.com" target="_parent">Example</a>
<!-- Use "_top" to open in the topmost frame -->
<a href="https://example.com" target="_top">Example</a>
Correct: Using a custom named browsing context
If you intend to target a specific named window or frame rather than using a keyword, remove the underscore prefix:
<!-- Valid: "myframe" is a custom browsing context name -->
<a href="https://example.com" target="myframe">Open in myframe</a>
<!-- Valid: targeting a named iframe -->
<iframe name="content-frame" src="about:blank"></iframe>
<a href="https://example.com" target="content-frame">Load in iframe</a>
A common mistake is using _new with the intention of opening a link in a new tab. While some browsers may treat _new similarly to _blank, it is not a valid keyword. Use _blank instead. Note that when using target="_blank", it's a good security practice to also include rel="noopener" (though modern browsers now do this by default):
<a href="https://example.com" target="_blank" rel="noopener">Example</a>
The type attribute on an <a> element is an advisory hint that tells the browser what media type (MIME type) to expect at the linked resource. A valid MIME type follows a strict format: a type, a / separator, and a subtype (e.g., text/html, application/pdf, image/png). Each part must consist of token characters — letters, digits, and certain symbols — but not spaces.
This validation error occurs when the MIME type value contains a space or other unexpected character in a position where only token characters or a / are allowed. Common causes include:
- Accidental spaces within the MIME type (e.g.,
application/ pdforapplication /pdf). - Multiple MIME types separated by spaces (e.g.,
text/html text/plain), which is not valid since the attribute accepts only a single MIME type. - Typos or copy-paste errors that introduce whitespace or non-token characters.
While the type attribute is purely advisory and browsers won't refuse to follow a link based on it, an invalid value defeats its purpose and signals sloppy markup. Standards-compliant HTML ensures your pages are interpreted consistently and avoids confusing tools, screen readers, or other user agents that may parse this attribute.
Examples
Incorrect: Space within the MIME type
<a href="report.pdf" type="application/ pdf">Download Report</a>
The space after the / makes this an invalid MIME type.
Incorrect: Multiple MIME types separated by a space
<a href="data.csv" type="text/csv text/plain">Download Data</a>
The type attribute only accepts a single MIME type. The space between text/csv and text/plain triggers the error.
Incorrect: Leading or trailing spaces
<a href="photo.jpg" type=" image/jpeg ">View Photo</a>
Spaces before or after the MIME type are not permitted.
Correct: Valid MIME type with no spaces
<a href="report.pdf" type="application/pdf">Download Report</a>
Correct: Other common valid MIME types
<a href="data.csv" type="text/csv">Download Data</a>
<a href="photo.jpg" type="image/jpeg">View Photo</a>
<a href="archive.zip" type="application/zip">Download Archive</a>
Correct: MIME type with a parameter
MIME types can include parameters separated by a semicolon — no spaces are required, though a single space after the semicolon is permitted per the MIME specification:
<a href="page.html" type="text/html; charset=utf-8">View Page</a>
How to Fix
- Inspect the
typevalue — look for any spaces within the type or subtype portions (before or after the/). - Remove extra spaces — ensure the value is a single, properly formatted MIME type like
type/subtype. - Use only one MIME type — if you've listed multiple types, pick the one that accurately describes the linked resource.
- Verify the MIME type is valid — consult the IANA Media Types registry to confirm you're using a recognized type.
- Consider removing the attribute — since
typeis purely advisory on<a>elements, if you're unsure of the correct MIME type, omitting the attribute entirely is perfectly valid.
A MIME type (also called a media type) always follows the format type/subtype, such as text/html, application/pdf, or image/jpeg. The "type" part indicates the general category (e.g., text, image, application, audio, video), and the "subtype" specifies the exact format within that category. When the validator reports "Subtype missing," it means the value you provided either lacks the /subtype portion or isn't a valid MIME type structure at all.
A common cause of this error is misunderstanding the purpose of the type attribute on <a> elements. The type attribute is not used to change the behavior or appearance of the link (the way type works on <input> or <button> elements). Instead, it serves as an advisory hint to the browser about what kind of resource the link points to. The browser may use this information to adjust its UI — for example, showing a download prompt for application/pdf — but it is not required to act on it.
Because of this misunderstanding, developers sometimes write type="button" on an <a> element, thinking it will make the link behave like a button. The value button is not a valid MIME type (it has no subtype), so the validator flags it. If you need a button, use a <button> element instead. If you need a styled link that looks like a button, keep the <a> element and use CSS for styling.
Why this matters
- Standards compliance: The HTML specification requires the
typeattribute on<a>to be a valid MIME type string. An invalid value violates the spec and may be ignored by browsers or cause unexpected behavior. - Accessibility and semantics: Using
type="button"on a link can create confusion about the element's role. Screen readers and assistive technologies rely on correct semantics to convey meaning to users. - Browser behavior: While browsers are generally forgiving, an invalid
typevalue provides no useful information and could interfere with how the browser handles the linked resource.
How to fix it
- If you intended to hint at the linked resource's MIME type, make sure you provide a complete
type/subtypevalue — for example,application/pdfrather than justapplication. - If you used
typeto try to style or change the link's behavior, remove thetypeattribute entirely. Use CSS for visual styling or switch to a more appropriate element like<button>. - If you don't need the
typeattribute, simply remove it. It's entirely optional on<a>elements.
Examples
Incorrect: missing subtype
<a href="report.pdf" type="application">Download report</a>
The value application is incomplete — it's missing the subtype portion after the slash.
Incorrect: not a MIME type at all
<a href="/order.php" type="button">Submit</a>
The value button is not a MIME type. This often stems from confusing the type attribute on <a> with the type attribute on <input> or <button>.
Correct: valid MIME type
<a href="report.pdf" type="application/pdf">Download report</a>
<a href="photo.jpeg" type="image/jpeg">See a photo</a>
The type attribute uses a properly formatted MIME type with both a type and subtype.
Correct: removing the attribute entirely
<a href="/order.php">Submit</a>
If the type attribute isn't serving a real purpose, the simplest fix is to remove it.
Correct: using a button element instead
<button type="submit">Submit</button>
If you need actual button behavior (such as submitting a form), use a <button> element rather than an <a> element with an invalid type.
The type attribute on a <button> only accepts three keywords — submit, reset, and button — so any other value is rejected.
type is an enumerated attribute, not a free-form one. submit posts the form (and is what a button does when the attribute is absent), reset clears the form fields, and button does nothing on its own and is meant to be wired up with JavaScript. Anything outside that set is invalid markup, and the browser falls back to submit, so a button you expected to stay inert may submit the form instead.
If you want the default behavior, leave the attribute off or write type="submit" explicitly; if the button should not submit, use type="button".
Invalid example
<button type="default">Save</button>
Valid example
<button type="submit">Save</button>
The type attribute on a <link> element specifies the MIME type of the linked resource. MIME types follow a specific format: a type and subtype separated by a single forward slash, like text/css, image/png, or application/json. They never contain the :// sequence found in URLs.
This error most commonly occurs when a URL is accidentally placed in the type attribute instead of in the href attribute, or when the attributes are confused with one another. For example, writing type="https://example.com/style.css" triggers this error because the validator encounters the colon in https: where it expects a valid MIME type token.
Another common cause is copying type values from other contexts (such as XML namespaces or schema references) that use URL-like strings, and mistakenly applying them to the type attribute.
Why this matters
- Standards compliance: The HTML specification requires the
typeattribute to contain a valid MIME type. Invalid values violate the spec and may cause browsers to misinterpret or ignore the linked resource. - Browser behavior: Browsers use the
typeattribute as a hint for how to handle the resource. An invalid MIME type could lead the browser to skip loading the resource entirely, causing missing styles, icons, or other assets. - Maintainability: Incorrect attribute values signal to other developers (and automated tools) that something is misconfigured, making the code harder to maintain.
How to fix it
- Check that
typecontains a valid MIME type, not a URL or other string. Common valid values includetext/css,image/png,image/x-icon,image/svg+xml, andapplication/rss+xml. - Ensure URLs are in the
hrefattribute, nottype. - Consider removing
typeentirely. For stylesheets, modern browsers default totext/css, sotype="text/css"is optional. For many use cases, thetypeattribute can be safely omitted.
Examples
❌ Incorrect: URL used as the type value
<link rel="stylesheet" type="https://example.com/style.css">
The validator sees the colon in https: and reports the error because this is a URL, not a MIME type.
❌ Incorrect: Attributes swapped
<link rel="icon" type="https://example.com/favicon.png" href="image/png">
Here the type and href values have been accidentally swapped.
✅ Correct: Valid MIME type with proper href
<link rel="icon" type="image/png" href="https://example.com/favicon.png">
✅ Correct: Stylesheet with valid type
<link rel="stylesheet" type="text/css" href="/css/style.css">
✅ Correct: Stylesheet without type (also valid)
<link rel="stylesheet" href="/css/style.css">
Since browsers default to text/css for stylesheets, omitting type is perfectly valid and keeps your markup cleaner.
✅ Correct: RSS feed link
<link rel="alternate" type="application/rss+xml" title="RSS Feed" href="/feed.xml">
A MIME type (also called a media type) is composed of two parts: a type and a subtype, separated by a forward slash (/) with no whitespace. For example, text/javascript has text as the type and javascript as the subtype. When you specify a value like text or javascript alone — without the slash and the other component — the validator reports this error because the subtype is missing.
This error commonly occurs when authors confuse the MIME type format with a simple label, writing something like type="text" or type="javascript" instead of the full type="text/javascript". It can also happen due to a typo, such as accidentally omitting the slash or the subtype portion.
Why this matters
Browsers rely on the type attribute to determine how to process the contents of a <script> element. An invalid MIME type can cause browsers to misinterpret or skip the script entirely. While modern browsers default to JavaScript when no type is specified, providing a malformed MIME type is not the same as omitting it — it may lead to unpredictable behavior across different browsers and versions. Keeping your markup valid also ensures better tooling support and forward compatibility.
How to fix it
You have two main options:
- Provide a complete, valid MIME type. For JavaScript, use
text/javascript. For JSON data blocks, useapplication/json. For importmaps, useimportmap. - Remove the
typeattribute entirely. Per the HTML specification, the default type for<script>istext/javascript, so omittingtypeis perfectly valid and is actually the recommended approach for standard JavaScript.
Examples
Incorrect: missing subtype
<!-- "text" alone is not a valid MIME type -->
<script type="text" src="app.js"></script>
<!-- "javascript" alone is not a valid MIME type -->
<script type="javascript" src="app.js"></script>
Correct: full MIME type specified
<script type="text/javascript" src="app.js"></script>
Correct: omitting the type attribute (recommended for JavaScript)
<script src="app.js"></script>
Since text/javascript is the default, omitting the attribute is the cleanest approach for standard JavaScript files.
Correct: using type for non-JavaScript purposes
The type attribute is still useful when embedding non-JavaScript content in a <script> element. In these cases, always use the full MIME type:
<script type="application/json" id="config">
{"apiUrl": "https://example.com/api"}
</script>
<script type="importmap">
{ "imports": { "lodash": "/libs/lodash.js" } }
</script>
Common valid MIME types for <script>
| MIME Type | Purpose |
|---|---|
text/javascript | Standard JavaScript (default) |
module | JavaScript module |
importmap | Import map |
application/json | Embedded JSON data |
application/ld+json | Linked Data / structured data |
Note that module and importmap are special values defined by the HTML specification and are not traditional MIME types, but they are valid values for the type attribute on <script> elements.
The version attribute on the <svg> element is obsolete in SVG 2 and is no longer recognized as a valid attribute by the W3C HTML validator.
The version attribute was used in SVG 1.0 and SVG 1.1 to indicate which specification the SVG content conformed to, with values like "1.0" or "1.1". However, SVG 2 — which is the version used when SVG is embedded in HTML5 documents — dropped this attribute entirely. It never had any practical effect on how browsers rendered SVG content, so removing it is safe and has no impact on functionality.
Similarly, the baseProfile attribute and the xmlns:xlink namespace declaration are also obsolete when using inline SVG in HTML5. You can safely remove all three.
Bad Example
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
Fixed Example
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
<circle cx="50" cy="50" r="40" fill="blue" />
</svg>
If your SVG files are exported from tools like Illustrator, Inkscape, or Figma, they often include the version attribute by default. You can safely strip it out manually or use an SVG optimizer like SVGO to clean up unnecessary attributes automatically.
According to the HTML specification, the width and height attributes on <iframe> elements accept only a valid non-negative integer — a string of one or more ASCII digits (0–9) with no decimal points, spaces, or unit suffixes like px. This is different from CSS, where properties like width and height accept decimal values and units. The HTML attributes represent dimensions in CSS pixels implicitly, so only bare whole numbers are allowed.
When the W3C validator reports "Expected a digit but saw '.' instead", it means it was parsing the attribute value character by character and encountered a period (.) where only digits are valid. This typically happens when authors copy computed or fractional values from design tools, JavaScript calculations, or CSS into HTML attributes.
Why this matters
- Standards compliance: Browsers may handle invalid attribute values inconsistently. While most modern browsers will parse and truncate decimal values gracefully, the behavior is not guaranteed and falls outside the specification.
- Predictable rendering: Relying on how browsers handle malformed values can lead to subtle differences across browser engines. Using valid integers ensures consistent behavior everywhere.
- Code quality: Clean, valid markup is easier to maintain and signals professionalism, which matters especially for shared codebases and collaborative projects.
How to fix it
- Round the value to the nearest whole number. Use standard rounding rules: round up if the decimal portion is
.5or greater, round down otherwise. - Remove any decimal point and trailing digits from the attribute value.
- If you need precise, fractional dimensions, use CSS instead of HTML attributes. CSS
widthandheightproperties accept decimal values with units (e.g.,602.88px).
Examples
❌ Invalid: decimal values in width and height
<iframe src="example.html" height="602.88" width="800.2"></iframe>
The validator will flag both attributes because 602.88 and 800.2 contain a . character.
✅ Fixed: whole number values
<iframe src="example.html" height="603" width="800"></iframe>
The decimal values have been rounded to the nearest integer: 602.88 becomes 603, and 800.2 becomes 800.
✅ Alternative: use CSS for precise dimensions
If you need exact fractional dimensions, move the sizing to CSS and remove the HTML attributes entirely:
<iframe src="example.html" style="height: 602.88px; width: 800.2px;"></iframe>
Or, better yet, use an external stylesheet:
<iframe src="example.html" class="content-frame"></iframe>
.content-frame {
width: 800.2px;
height: 602.88px;
}
❌ Invalid: other non-digit characters
This error can also appear if you include units in the attribute value:
<iframe src="example.html" width="800px" height="600px"></iframe>
✅ Fixed: remove the units
<iframe src="example.html" width="800" height="600"></iframe>
The same rule applies to the <img>, <video>, <canvas>, and other elements that accept width and height as HTML attributes — they all expect valid non-negative integers without decimals or units.
According to the HTML specification, the width and height attributes on img elements accept only valid non-negative integers. A valid non-negative integer consists of one or more ASCII digits (0–9) with no other characters — no decimal points, no spaces, no units like px. When the validator encounters a value such as 602.88, it parses the digits 602 successfully, then hits the . character where it expects another digit or the end of the value, triggering the error.
This issue commonly arises when dimension values are generated programmatically — for example, when a CMS, image processing tool, or JavaScript calculation produces floating-point numbers and outputs them directly into the HTML. It can also happen when copying dimension values from CSS or design tools that work in sub-pixel units.
Why this matters
- Standards compliance: The HTML specification is explicit that these attributes take integer values. Using decimals produces invalid markup.
- Unpredictable rendering: Browsers may handle the malformed value in different ways — some might truncate at the decimal point, others might ignore the attribute entirely. This can lead to layout shifts or incorrectly sized images.
- Layout stability: The
widthandheightattributes are used by browsers to calculate the aspect ratio of an image before it loads, which helps prevent Cumulative Layout Shift (CLS). Invalid values can undermine this behavior, causing content to jump around as images load.
How to fix it
- Round to the nearest integer. If your value is
602.88, round it to603. If it's800.2, round to800. - Remove the decimal point entirely. The value must contain only digits.
- Do not include units. Values like
800pxare also invalid; use just800. - Fix the source of the values. If your CMS or build tool generates these attributes, update the logic to output integers (e.g., using
Math.round()in JavaScript orround()in PHP/Python).
Examples
❌ Incorrect: decimal values in width and height
<img src="photo.jpg" alt="A golden retriever" height="602.88" width="800.2">
The validator reports errors for both attributes because . is not a valid character in a non-negative integer.
✅ Correct: whole number values
<img src="photo.jpg" alt="A golden retriever" height="603" width="800">
Both values are valid non-negative integers with no decimal points.
❌ Incorrect: trailing decimal point with no fractional part
<img src="banner.png" alt="Sale banner" width="1200." height="400.">
Even a trailing . with nothing after it is invalid — the parser still encounters an unexpected character.
✅ Correct: clean integer values
<img src="banner.png" alt="Sale banner" width="1200" height="400">
Using CSS for sub-pixel precision
If you genuinely need sub-pixel sizing (which is rare for images), use CSS instead of HTML attributes. CSS width and height properties do accept decimal values:
<img src="icon.svg" alt="Settings icon" style="width: 24.5px; height: 24.5px;">
However, keep in mind that you should still provide integer width and height HTML attributes for aspect ratio hinting, and then override with CSS if sub-pixel precision is needed:
<img
src="icon.svg"
alt="Settings icon"
width="25"
height="25"
style="width: 24.5px; height: 24.5px;">
This approach gives you valid HTML, proper aspect ratio hints for layout stability, and the precise sizing you need.
The HTML specification defines the width and height attributes on <embed> as accepting only valid non-negative integers. This means bare numbers like 600 or 800 that represent dimensions in CSS pixels. When you write width="100%", the validator expects a digit character but encounters the % sign, which doesn't conform to the expected format.
This matters for several reasons. Browsers may interpret invalid attribute values inconsistently — some might ignore the percentage and fall back to a default size, while others might attempt to parse the number portion and discard the %. This leads to unpredictable rendering across different browsers and devices. Following the specification ensures your embedded content displays at predictable dimensions everywhere.
The same rule applies to the height attribute. Neither width nor height on <embed> supports units of any kind — no px, %, em, or other suffixes. Just a plain integer.
How to Fix It
You have two main approaches:
Use integer pixel values directly. Replace
width="100%"with a specific pixel value likewidth="800". This is the simplest fix when you know the desired dimensions.Use CSS for responsive or percentage-based sizing. Remove the
widthandheightattributes (or set them to reasonable defaults) and apply CSS through aclass,styleattribute, or external stylesheet. This is the better approach when you need the embed to be fluid or responsive.
Examples
Invalid — percentage in the width attribute
This triggers the validator error because 100% is not a valid non-negative integer:
<embed src="file.pdf" type="application/pdf" width="100%" height="600">
Fixed — using pixel values in attributes
Replace the percentage with a plain integer:
<embed src="file.pdf" type="application/pdf" width="800" height="600">
Fixed — using CSS for percentage-based sizing
Remove the dimension attributes and use CSS to control the size:
<embed src="file.pdf" type="application/pdf" class="embed-fluid">
.embed-fluid {
width: 100%;
height: 600px;
}
Fixed — responsive embed with a wrapper container
For a fully responsive embed that maintains an aspect ratio, wrap it in a container and use CSS:
<div class="embed-wrapper">
<embed src="file.pdf" type="application/pdf">
</div>
.embed-wrapper {
width: 100%;
max-width: 960px;
aspect-ratio: 4 / 3;
}
.embed-wrapper embed {
width: 100%;
height: 100%;
}
This approach gives you full control over sizing and responsiveness without relying on invalid HTML attributes. The aspect-ratio property ensures the container (and therefore the embed) maintains consistent proportions as it scales.
The HTML specification defines the width and height attributes on <iframe> as accepting only valid non-negative integers. These values are interpreted as pixel dimensions. Unlike some older HTML practices where percentage values were sometimes accepted by browsers, the current standard does not permit the % character in these attributes. When the W3C validator encounters a value like "100%", it expects every character to be a digit and flags the % as invalid.
This is a standards compliance issue, but it also affects predictability across browsers. While most modern browsers may still interpret width="100%" on an <iframe> as you'd expect, this behavior is non-standard and not guaranteed. Relying on it means your layout could break in certain browsers or rendering modes. Using CSS for percentage-based sizing is the correct, reliable approach.
How to Fix It
If you need a fixed pixel width, simply provide the integer value without any unit:
<iframe src="page.html" width="600" height="400"></iframe>
If you need a percentage-based width (e.g., to make the iframe responsive), remove the width attribute entirely and use CSS instead. You can apply styles inline or through a stylesheet.
Inline style approach:
<iframe src="page.html" style="width: 100%; height: 400px;"></iframe>
CSS class approach:
<iframe src="page.html" class="responsive-iframe"></iframe>
.responsive-iframe {
width: 100%;
height: 400px;
}
This same rule applies to the height attribute — values like height="50%" are equally invalid and should be handled through CSS.
Examples
❌ Invalid: Percentage in width attribute
<iframe src="https://example.com" width="100%" height="300"></iframe>
This triggers the error because 100% is not a valid non-negative integer.
❌ Invalid: Percentage in both width and height
<iframe src="https://example.com" width="100%" height="50%"></iframe>
Both attributes contain invalid values due to the % character.
✅ Valid: Fixed pixel values using attributes
<iframe src="https://example.com" width="800" height="300"></iframe>
Both values are valid non-negative integers representing pixels.
✅ Valid: Percentage sizing using CSS
<iframe src="https://example.com" style="width: 100%; height: 300px;"></iframe>
The percentage is handled by CSS, and no invalid attributes are present.
✅ Valid: Responsive iframe with a wrapper
For a fully responsive iframe that maintains an aspect ratio, a common pattern uses a wrapper element:
<div style="position: relative; width: 100%; aspect-ratio: 16 / 9;">
<iframe
src="https://example.com"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0;">
</iframe>
</div>
This approach keeps the HTML valid while giving you full control over the iframe's responsive behavior through CSS.
The HTML specification defines the width attribute on <img> elements as a "valid non-negative integer" — essentially a string of digits with no units, no decimals, and no percentage signs. When you write something like width="100%", the validator expects a digit character but encounters %, producing this error. While some older browsers historically accepted percentage values in the width attribute (a holdover from pre-HTML5 conventions), this was never part of the modern HTML standard and should not be relied upon.
This matters for several reasons. First, standards compliance ensures your markup behaves consistently across browsers and devices. Second, assistive technologies and browser layout engines may interpret an invalid width value unpredictably — some may ignore it entirely, others may parse it incorrectly. Third, the width and height attributes on <img> serve an important role in reserving layout space before the image loads (preventing Cumulative Layout Shift), but they only work correctly when set to valid integer pixel values that reflect the image's intrinsic or intended pixel dimensions.
How to fix it
If you want a fixed pixel width, provide just the integer without any unit:
<img src="photo.jpg" alt="A red car" width="600">
If you need a percentage-based or responsive width, remove the width attribute from the HTML and use CSS instead. You can apply the style inline, via a <style> block, or in an external stylesheet.
If you want to preserve aspect ratio and prevent layout shift, keep the width and height attributes set to values that represent the image's intrinsic aspect ratio (in pixels), and then override the displayed size with CSS.
Examples
❌ Invalid: percentage in the width attribute
<img src="photo.jpg" alt="A red car" width="100%">
This triggers the error because 100% is not a valid non-negative integer.
❌ Invalid: other non-integer values
<img src="photo.jpg" alt="A red car" width="50%">
<img src="banner.jpg" alt="Sale banner" width="300px">
<img src="icon.png" alt="Settings icon" width="2.5">
Units like px, percentage signs, and decimal points are all invalid in the width attribute.
✅ Fixed: using a pixel integer
<img src="photo.jpg" alt="A red car" width="800" height="600">
✅ Fixed: percentage width via inline CSS
<img src="photo.jpg" alt="A red car" style="width: 100%;">
✅ Fixed: responsive image with preserved aspect ratio
This approach sets the intrinsic dimensions in the attributes (to reserve layout space) and uses CSS to make the image responsive:
<style>
.responsive-img {
width: 100%;
height: auto;
}
</style>
<img src="photo.jpg" alt="A red car" width="800" height="600" class="responsive-img">
The browser uses the width and height attribute values to calculate the aspect ratio and reserve the correct amount of space, while CSS controls the actual rendered size. This is the recommended approach for responsive images because it avoids layout shift while still allowing flexible sizing.
The HTML specification defines the width attribute on <video> as a "valid non-negative integer," which means it must consist only of digits (e.g., 640). It cannot include units like px, em, or %. When you write something like width="100%", the validator expects a digit character but encounters the % sign, producing this error.
This is a common mistake because CSS allows percentage values for width, and some older HTML elements (like <table>) historically accepted percentage values in their width attributes. However, the <video> element follows the modern HTML specification, which restricts width to pixel integers only.
Why this matters
- Standards compliance: Browsers may interpret invalid attribute values unpredictably. While most modern browsers might ignore the
%and attempt to parse the number, this behavior is not guaranteed. - Responsive design intent is lost: Even if a browser tries to handle
width="100%", it may treat it aswidth="100"(100 CSS pixels), which is almost certainly not what you intended. - Accessibility and consistency: Valid markup ensures assistive technologies and all browsers render your content as expected.
How to fix it
If you need a fixed pixel width, set the width attribute to a plain integer. If you need a responsive or percentage-based width, remove the width attribute entirely and use CSS.
Examples
❌ Invalid: percentage value in the width attribute
<video controls width="100%">
<source src="/media/video.mp4" type="video/mp4">
</video>
✅ Fixed: using a pixel integer for a fixed width
<video controls width="640" height="360">
<source src="/media/video.mp4" type="video/mp4">
</video>
✅ Fixed: using CSS for a percentage-based width
<video controls style="width: 100%;">
<source src="/media/video.mp4" type="video/mp4">
</video>
✅ Fixed: using an external stylesheet for responsive video
<style>
.responsive-video {
width: 100%;
max-width: 800px;
height: auto;
}
</style>
<video controls class="responsive-video">
<source src="/media/video.mp4" type="video/mp4">
</video>
The CSS approach is generally preferred for responsive layouts because it gives you much more control — you can combine width, max-width, and height: auto to create a video that scales proportionally within its container. The width and height HTML attributes are best used when you want to specify the video's intrinsic dimensions in pixels, which also helps the browser reserve the correct amount of space before the video loads, reducing layout shifts.
According to the HTML Living Standard, the width and height attributes on the <object> element accept only valid non-negative integers — plain numbers representing pixels, such as 600 or 400. The validator expects each character in the value to be a digit (0–9). When it encounters a % sign, it reports "Expected a digit but saw '%' instead."
This is different from some legacy HTML 4 behavior where certain elements accepted percentage values in dimension attributes. In modern HTML, the <object> element's dimension attributes are strictly pixel-only. The same restriction applies to elements like <img>, <video>, and <canvas>.
Why this matters
- Standards compliance: Browsers may still render percentage values in these attributes, but the behavior is not defined by the specification and cannot be relied upon across browsers or future versions.
- Predictable rendering: Pixel values in attributes give the browser a concrete intrinsic size for the object, which helps with layout calculations and prevents content reflow as the page loads.
- Accessibility and tooling: Assistive technologies and other tools that parse HTML rely on well-formed attribute values. Invalid values may cause unexpected behavior.
How to fix it
You have two options:
- Use pixel values in the attributes if you know the exact dimensions you need.
- Use CSS if you need percentage-based or responsive sizing. Remove the
widthandheightattributes (or set them to pixel fallback values) and apply CSSwidthandheightproperties instead.
When using CSS for 100% height, remember that percentage heights require the parent elements to also have a defined height. This typically means setting height: 100% on html and body as well.
Examples
❌ Invalid: percentage values in attributes
<object data="example.pdf" type="application/pdf" width="100%" height="100%"></object>
The validator flags both width="100%" and height="100%" because % is not a digit.
✅ Fixed: pixel values in attributes
<object data="example.pdf" type="application/pdf" width="600" height="400"></object>
Plain integer values are valid and give the object a fixed size in pixels.
✅ Fixed: percentage sizing with CSS
<object
data="example.pdf"
type="application/pdf"
style="width: 100%; height: 500px;">
</object>
Using inline CSS allows you to mix units freely, including percentages, vh, em, and more.
✅ Fixed: full-page object with CSS
When you need the <object> to fill the entire viewport, use a stylesheet to set heights on the ancestor elements:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Full-Page Object Example</title>
<style>
html, body {
height: 100%;
margin: 0;
}
object {
display: block;
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<object data="example.pdf" type="application/pdf"></object>
</body>
</html>
This approach is fully valid, responsive, and gives you much more control over sizing than HTML attributes alone.
Why This Matters
While HTML5 is quite permissive with id values (allowing almost anything except spaces), elements within XML-based vocabularies like SVG and MathML are held to stricter rules. When these elements appear in your HTML document, their attributes must still conform to XML 1.0 naming conventions as defined by the relevant specification.
XML 1.0 names must follow these rules:
- Must start with a letter (
a–z,A–Z) or an underscore (_) - Subsequent characters can be letters, digits (
0–9), hyphens (-), underscores (_), and periods (.) - Cannot contain spaces, colons (outside of namespaced contexts), or special characters like
@,#,$,!, etc.
This error typically appears when design tools (such as Figma, Illustrator, or Sketch) export SVG files with auto-generated id values that include spaces or other invalid characters. Browsers may still render the content, but relying on non-conformant names can cause problems with CSS selectors, JavaScript's getElementById(), URL fragment references, and accessibility tools that depend on valid identifiers.
How to Fix It
- Remove spaces — replace them with hyphens or underscores, or use camelCase.
- Ensure the name starts with a letter or underscore — if it starts with a digit, prefix it with a letter or underscore.
- Strip out special characters — remove or replace characters like
@,#,(,), etc. - Review exported SVG files — if you're embedding SVGs from design tools, clean up the generated
idvalues before adding them to your HTML.
Examples
Invalid: Space in the id value
The space in "Group 270" makes this an invalid XML 1.0 name:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g id="Group 270">
<circle cx="50" cy="50" r="40" />
</g>
</svg>
Invalid: Name starts with a digit
XML 1.0 names cannot begin with a number:
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect id="1st-rectangle" width="100" height="50" />
</svg>
Invalid: Special characters in the name
Characters like ( and ) are not allowed:
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<path id="icon(home)" d="M10 80 L50 10 L90 80 Z" />
</svg>
Fixed: Valid XML 1.0 names
Replace spaces with hyphens, prefix digit-leading names with a letter, and remove special characters:
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<g id="group-270">
<circle cx="50" cy="50" r="40" />
</g>
<rect id="first-rectangle" width="100" height="50" />
<path id="icon-home" d="M10 80 L50 10 L90 80 Z" />
</svg>
Tip: Cleaning up exported SVGs
Design tools often produce id values like "Frame 42", "Vector (Stroke)", or "123_layer". A quick find-and-replace workflow can fix these before they land in your codebase. You can also use tools like SVGO to optimize and clean up SVG output, including stripping or renaming invalid identifiers.
The <area> element defines a clickable region within an image map (<map>). The coords attribute works together with the shape attribute to describe the geometry of that region. When the coordinates don't conform to the rules for the given shape, the browser may ignore the area entirely or interpret it unpredictably, making the clickable region inaccessible to users.
Each shape type has strict requirements:
- Rectangle (
shape="rect"): Requires exactly four integers in the formatx1,y1,x2,y2, wherex1,y1is the top-left corner andx2,y2is the bottom-right corner. Because0,0is the top-left of the image,x1must be less thanx2andy1must be less thany2. - Circle (
shape="circle"): Requires exactly three integers in the formatx,y,r, wherex,yis the center of the circle andris the radius. The radius must be a positive integer, and the first coordinate (the x-center) must be less than the third value (the radius) is not required—but the validator message mentions this constraint to flag cases where values appear swapped or malformed. - Polygon (
shape="poly"): Requires at least six integers (threex,ycoordinate pairs), forming a polygon with at least three vertices (a triangle). The format isx1,y1,x2,y2,...,xn,yn, and the number of integers must be even since they represent pairs.
Getting these formats wrong is a standards compliance issue. Assistive technologies such as screen readers rely on valid <area> definitions to convey interactive regions to users. Invalid coordinates can also cause the clickable area to silently fail in some browsers.
Examples
Invalid: Rectangle with swapped coordinates
The top-left corner values are larger than the bottom-right corner values:
<map name="nav">
<area shape="rect" coords="200,150,50,10" href="/home" alt="Home">
</map>
Fixed: Rectangle with correct coordinate order
<map name="nav">
<area shape="rect" coords="50,10,200,150" href="/home" alt="Home">
</map>
Invalid: Circle with wrong number of values
Four values are provided instead of the required three:
<map name="nav">
<area shape="circle" coords="100,75,50,25" href="/info" alt="Info">
</map>
Fixed: Circle with three values
<map name="nav">
<area shape="circle" coords="100,75,50" href="/info" alt="Info">
</map>
Invalid: Polygon with too few coordinates
Only four integers (two coordinate pairs) are provided, but a polygon needs at least three pairs:
<map name="nav">
<area shape="poly" coords="10,20,30,40" href="/about" alt="About">
</map>
Fixed: Polygon with at least three coordinate pairs
<map name="nav">
<area shape="poly" coords="10,20,30,40,20,60" href="/about" alt="About">
</map>
Invalid: Non-integer or malformed values
Decimal numbers and spaces in the wrong places will also trigger this error:
<map name="nav">
<area shape="rect" coords="10.5, 20, 100, 200" href="/page" alt="Page">
</map>
Fixed: Using only comma-separated integers
<map name="nav">
<area shape="rect" coords="10,20,100,200" href="/page" alt="Page">
</map>
Complete valid image map example
<img src="floorplan.png" alt="Office floor plan" usemap="#office">
<map name="office">
<area shape="rect" coords="0,0,150,100" href="/lobby" alt="Lobby">
<area shape="circle" coords="200,150,40" href="/meeting-room" alt="Meeting room">
<area shape="poly" coords="300,50,400,50,400,150,350,200,300,150" href="/lounge" alt="Lounge">
</map>
When debugging coordinate issues, double-check that the shape attribute matches the number of coordinates you've provided, that all values are non-negative integers separated by commas with no extra spaces, and that rectangle corners are specified in the correct top-left to bottom-right order.
In HTML, the width and height attributes on elements like <img> and <iframe> are defined as accepting only valid non-negative integers. According to the HTML specification, the value is implicitly in CSS pixels, so appending px or any other unit is both unnecessary and invalid. The parser expects every character in the value to be a digit (0–9), and when it encounters a letter like p, it reports the error.
This is a common mistake, especially for developers who frequently work with CSS, where px units are required. In HTML attributes, however, the convention is different — the pixel unit is implied, and adding it creates a malformed value. Browsers may still attempt to parse the number by ignoring the trailing characters, but this behavior is not guaranteed and should not be relied upon.
Getting these attributes right matters for several reasons:
- Standards compliance ensures your markup is predictable and portable across all browsers and user agents.
- Layout stability depends on the browser correctly reading
widthandheightto reserve space for images and iframes before they load, preventing cumulative layout shift (CLS). A malformed value could cause the browser to fall back to default sizing or ignore the attribute entirely. - Accessibility tools and screen readers may use these attributes to convey information about embedded content, and invalid values could interfere with that process.
If you need to set dimensions using units other than pixels (such as percentages or viewport units), use CSS instead of HTML attributes.
Examples
❌ Invalid: using px in the attribute value
<img src="cat.jpg" alt="A cat sitting on a windowsill" width="225px" height="100px">
The validator reports an error because 225px and 100px contain the non-digit characters px.
✅ Valid: plain integers without units
<img src="cat.jpg" alt="A cat sitting on a windowsill" width="225" height="100">
❌ Invalid: using percentage in the attribute value
<iframe src="embed.html" width="100%" height="400px" title="Embedded content"></iframe>
Both 100% and 400px are invalid because they contain non-digit characters.
✅ Valid: plain integers on an <iframe>
<iframe src="embed.html" width="800" height="400" title="Embedded content"></iframe>
✅ Using CSS when you need non-pixel units
If you need percentage-based or responsive sizing, apply it through CSS rather than HTML attributes:
<iframe src="embed.html" style="width: 100%; height: 400px;" title="Embedded content"></iframe>
Or better yet, use an external stylesheet:
<style>
.responsive-frame {
width: 100%;
height: 400px;
}
</style>
<iframe src="embed.html" class="responsive-frame" title="Embedded content"></iframe>
Quick reference of invalid vs. valid values
| Invalid value | Problem | Valid alternative |
|---|---|---|
225px | Contains px | 225 |
100% | Contains % | Use CSS instead |
20em | Contains em | Use CSS instead |
auto | Not a number | Use CSS instead |
10.5 | Decimal point | 10 or 11 |
The fix is straightforward: strip any unit suffixes from width and height HTML attributes and provide plain integer values. For anything beyond simple pixel dimensions, move your sizing logic to CSS.
The language tag yaml is reserved by IANA and cannot be used as a value for the lang attribute, which expects a valid BCP 47 language tag (like en for English or fr for French).
The lang attribute specifies the natural language of an element's content — human languages like English, Spanish, or Japanese. It is not meant to indicate a programming or markup language. When you write lang="yaml" on a <code> element, the validator rejects it because yaml is a reserved IANA subtag with no valid use in BCP 47.
If your goal is to identify the code language for syntax highlighting or styling purposes, use the class attribute instead. A common convention, recommended by the HTML specification itself, is to use a class prefixed with language-, such as class="language-yaml".
HTML Examples
❌ Invalid: using lang for code language
<pre>
<code lang="yaml">
name: my-project
version: 1.0.0
</code>
</pre>
✅ Valid: using class for code language
<pre>
<code class="language-yaml">
name: my-project
version: 1.0.0
</code>
</pre>
This class="language-*" convention is widely supported by syntax highlighting libraries like Prism.js and highlight.js.
The allowfullscreen attribute on an <iframe> is a boolean attribute and does not accept a value like "yes".
Boolean attributes in HTML work by their presence or absence alone. When a boolean attribute is present on an element, it means "true." When it is absent, it means "false." Valid ways to write a boolean attribute are: the attribute name with no value, an empty string value (""), or the attribute name itself as the value. Assigning "yes", "true", or any other string is invalid.
This applies to all boolean attributes in HTML, such as disabled, checked, autoplay, muted, and allowfullscreen.
Invalid example
<iframe
src="https://example.com/video"
allowfullscreen="yes">
</iframe>
Valid example
Any of these three forms is valid:
<!-- Attribute name only (most common) -->
<iframe
src="https://example.com/video"
allowfullscreen>
</iframe>
<!-- Empty string value -->
<iframe
src="https://example.com/video"
allowfullscreen="">
</iframe>
<!-- Attribute name as the value -->
<iframe
src="https://example.com/video"
allowfullscreen="allowfullscreen">
</iframe>
The first form, with just allowfullscreen and no value, is the most widely used and the most readable.
The HTML specification defines a specific set of valid values for the type attribute on <input> elements, including text, number, email, tel, url, date, password, search, hidden, checkbox, radio, file, submit, reset, button, image, range, color, and others. The value "zip" is not among them. When a browser encounters an unrecognized type value, it falls back to type="text" — so the input may appear to work, but the markup is invalid and you lose the opportunity to leverage built-in browser features for better user experience.
This matters for several reasons. Invalid HTML can cause unpredictable behavior across different browsers and assistive technologies. Screen readers and other tools rely on valid markup to convey the purpose of form controls to users. Additionally, using the correct combination of valid attributes allows browsers to show optimized keyboards on mobile devices (e.g., a numeric keypad for ZIP codes) and to autofill values intelligently.
For ZIP or postal code fields, the best approach is to use type="text" combined with the autocomplete="postal-code" attribute, which tells browsers exactly what kind of data is expected. You can further enhance the input with inputmode="numeric" to trigger a numeric keyboard on mobile devices (for purely numeric ZIP codes like in the US) and a pattern attribute for client-side validation.
Examples
❌ Invalid: Using type="zip"
<label for="zip">ZIP Code</label>
<input type="zip" id="zip" name="zip">
This triggers the validation error because "zip" is not a valid value for the type attribute.
✅ Valid: Using type="text" with appropriate attributes (US ZIP code)
<label for="zip">ZIP Code</label>
<input
type="text"
id="zip"
name="zip"
inputmode="numeric"
pattern="[0-9]{5}(-[0-9]{4})?"
autocomplete="postal-code"
placeholder="12345"
aria-describedby="zip-hint">
<span id="zip-hint">5-digit ZIP code (e.g., 12345 or 12345-6789)</span>
This approach uses type="text" to remain valid, inputmode="numeric" to prompt a numeric keyboard on mobile, pattern for client-side format validation, and autocomplete="postal-code" so browsers can autofill the field correctly.
✅ Valid: International postal code field
<label for="postal">Postal Code</label>
<input
type="text"
id="postal"
name="postal_code"
autocomplete="postal-code">
For international postal codes that may contain letters (e.g., UK, Canada), omit inputmode="numeric" and use a broader or no pattern, since formats vary widely by country.
Why not type="number"?
You might be tempted to use type="number" for ZIP codes, but this is discouraged. type="number" is designed for values that represent a quantity — it may strip leading zeros (turning "01234" into "1234"), add increment/decrement spinner buttons, and behave unexpectedly with non-numeric postal codes. Always use type="text" for ZIP and postal codes.
The HTML parser has specific rules for how it handles sequences that begin with <. When it encounters <! followed by something other than -- (which starts a comment) or DOCTYPE (case-insensitive), the parser doesn't know how to interpret it. According to the WHATWG HTML Living Standard, such sequences are treated as "bogus comments" — the parser will try to recover by consuming content until it finds a > character, treating everything in between as a comment node. While browsers handle this gracefully through error recovery, the underlying markup is invalid and may not behave as intended.
Several common patterns trigger this error:
- Malformed comment delimiters: Adding a space between
<!and--, using only one hyphen (<!- comment ->), or forgetting the closing--before>. - Stray
<!sequences: Accidentally typing<!in your markup without a valid keyword following it, such as<!something>. - XML processing instructions: Using
<?xml version="1.0"?>or similar<?...?>syntax in an HTML document. Processing instructions are valid in XML/XHTML but are treated as bogus comments in HTML. - Mistyped doctype: Writing something like
<!DOCKTYPE html>instead of<!DOCTYPE html>. - Template or server-side artifacts: Server-side code or templating engines sometimes output fragments like
<!-->or<![]>that the HTML parser cannot interpret.
This matters for several reasons. First, since the parser consumes everything up to the next > as a bogus comment, actual content or markup could be swallowed and hidden from the rendered page. Second, different parsers may recover from these errors in slightly different ways, leading to inconsistent rendering. Third, invalid markup can interfere with assistive technologies that rely on a well-formed DOM.
To fix the issue, locate the flagged line in your HTML source and ensure that:
- All comments begin with exactly
<!--(no spaces or missing hyphens) and end with exactly-->. - Your
<!DOCTYPE html>declaration is correctly spelled. - You haven't included XML processing instructions (
<?...?>) in an HTML document. - No stray
<!or<?characters appear in your markup.
Examples
Malformed comment delimiter
<!-- ❌ Space between <! and -- -->
<! -- This is not a valid comment -->
<!-- ❌ Single hyphen instead of double -->
<!- This is not valid either ->
<!-- ✅ Correct comment syntax -->
<!-- This is a valid comment -->
XML processing instruction in HTML
<!-- ❌ Processing instructions are not valid in HTML -->
<?xml version="1.0" encoding="UTF-8"?>
<p>Hello</p>
<!-- ✅ Remove the processing instruction; it's not needed in HTML -->
<p>Hello</p>
Mistyped DOCTYPE
<!-- ❌ Misspelled DOCTYPE triggers bogus comment -->
<!DOCKTYPE html>
<!-- ✅ Correct spelling -->
<!DOCTYPE html>
Stray <! sequence
<!-- ❌ Invalid use of <! -->
<!if condition>
<p>Conditional content</p>
<!endif>
<!-- ✅ Use standard HTML comments for conditional notes -->
<!-- condition: start -->
<p>Conditional content</p>
<!-- condition: end -->
Empty or broken comment
<!-- ❌ Incomplete comment syntax -->
<!>
<p>Content</p>
<!-- ❌ Another broken variant -->
<!--->
<p>Content</p>
<!-- ✅ Either remove it or write a proper comment -->
<!-- placeholder -->
<p>Content</p>
Conditional comments (legacy IE syntax)
Conditional comments like <!--[if IE]> were a proprietary feature of Internet Explorer. While they don't typically trigger a bogus comment error (since they start with <!--), related patterns like <![if IE]> (without the --) will. Since IE conditional comments are no longer supported by any modern browser, the best fix is to remove them entirely.
<!-- ❌ Non-comment conditional syntax -->
<![if IE]>
<link rel="stylesheet" href="ie.css">
<![endif]>
<!-- ✅ Remove legacy conditional comments -->
<link rel="stylesheet" href="styles.css">
The HTML document's character encoding was not declared before the parser encountered non-ASCII content, forcing the validator to restart parsing with UTF-8 encoding.
When a browser or validator processes an HTML document, it needs to know the character encoding as early as possible. If the encoding isn't declared — or is declared too late in the document — the parser may initially guess the wrong encoding and then have to restart when it detects UTF-8 content. This warning typically appears when:
- The
<meta charset="utf-8">declaration is missing entirely. - The
<meta charset="utf-8">tag is placed after other elements like<title>or<script>that contain non-ASCII characters (e.g., accented letters, emoji, or special symbols). - The server sends a conflicting or missing
Content-TypeHTTP header.
The <meta charset="utf-8"> tag must appear within the first 1024 bytes of the document and should be the first child of the <head> element, before any other elements that contain text content.
Incorrect Example
<!DOCTYPE html>
<html lang="fr">
<head>
<title>Café résumé</title>
<meta charset="utf-8">
</head>
<body>
<p>Bienvenue au café!</p>
</body>
</html>
Here, the <title> contains non-ASCII characters (é) before the charset declaration, triggering the reparsing warning.
Fixed Example
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>Café résumé</title>
</head>
<body>
<p>Bienvenue au café!</p>
</body>
</html>
Moving <meta charset="utf-8"> to the very first position inside <head> ensures the parser knows the encoding before it encounters any non-ASCII characters.
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