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.
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
<iframesrc="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
<iframesrc="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:
<iframesrc="example.html"style="height:602.88px;width:800.2px;"></iframe>
Or, better yet, use an external stylesheet:
<iframesrc="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:
<iframesrc="example.html"width="800px"height="600px"></iframe>
✅ Fixed: remove the units
<iframesrc="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
<imgsrc="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
<imgsrc="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
<imgsrc="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
<imgsrc="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:
<imgsrc="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:
<embedsrc="file.pdf"type="application/pdf"width="100%"height="600">
Fixed — using pixel values in attributes
Replace the percentage with a plain integer:
<embedsrc="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:
<embedsrc="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:
<divclass="embed-wrapper">
<embedsrc="file.pdf"type="application/pdf">
</div>
.embed-wrapper{
width:100%;
max-width:960px;
aspect-ratio:4/3;
}
.embed-wrapperembed{
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:
<iframesrc="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:
<iframesrc="page.html"style="width:100%;height:400px;"></iframe>
CSS class approach:
<iframesrc="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
<iframesrc="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
<iframesrc="https://example.com"width="100%"height="50%"></iframe>
Both attributes contain invalid values due to the % character.
✅ Valid: Fixed pixel values using attributes
<iframesrc="https://example.com"width="800"height="300"></iframe>
Both values are valid non-negative integers representing pixels.
✅ Valid: Percentage sizing using CSS
<iframesrc="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:
<divstyle="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:
<imgsrc="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
<imgsrc="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
<imgsrc="photo.jpg"alt="A red car"width="50%">
<imgsrc="banner.jpg"alt="Sale banner"width="300px">
<imgsrc="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
<imgsrc="photo.jpg"alt="A red car"width="800"height="600">
✅ Fixed: percentage width via inline CSS
<imgsrc="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>
<imgsrc="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
<videocontrolswidth="100%">
<sourcesrc="/media/video.mp4"type="video/mp4">
</video>
✅ Fixed: using a pixel integer for a fixed width
<videocontrolswidth="640"height="360">
<sourcesrc="/media/video.mp4"type="video/mp4">
</video>
✅ Fixed: using CSS for a percentage-based width
<videocontrolsstyle="width:100%;">
<sourcesrc="/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>
<videocontrolsclass="responsive-video">
<sourcesrc="/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
<objectdata="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
<objectdata="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>
<htmllang="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>
<objectdata="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:
<svgviewBox="0 0 100 100"xmlns="http://www.w3.org/2000/svg">
<gid="Group 270">
<circlecx="50"cy="50"r="40"/>
</g>
</svg>
Invalid: Name starts with a digit
XML 1.0 names cannot begin with a number:
<svgviewBox="0 0 100 100"xmlns="http://www.w3.org/2000/svg">
<rectid="1st-rectangle"width="100"height="50"/>
</svg>
Invalid: Special characters in the name
Characters like ( and ) are not allowed:
<svgviewBox="0 0 200 100"xmlns="http://www.w3.org/2000/svg">
<pathid="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:
<svgviewBox="0 0 200 100"xmlns="http://www.w3.org/2000/svg">
<gid="group-270">
<circlecx="50"cy="50"r="40"/>
</g>
<rectid="first-rectangle"width="100"height="50"/>
<pathid="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:
<mapname="nav">
<areashape="rect"coords="200,150,50,10"href="/home"alt="Home">
</map>
Fixed: Rectangle with correct coordinate order
<mapname="nav">
<areashape="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:
<mapname="nav">
<areashape="circle"coords="100,75,50,25"href="/info"alt="Info">
</map>
Fixed: Circle with three values
<mapname="nav">
<areashape="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:
<mapname="nav">
<areashape="poly"coords="10,20,30,40"href="/about"alt="About">
</map>
Fixed: Polygon with at least three coordinate pairs
<mapname="nav">
<areashape="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:
<mapname="nav">
<areashape="rect"coords="10.5, 20, 100, 200"href="/page"alt="Page">
</map>
Fixed: Using only comma-separated integers
<mapname="nav">
<areashape="rect"coords="10,20,100,200"href="/page"alt="Page">
</map>
Complete valid image map example
<imgsrc="floorplan.png"alt="Office floor plan"usemap="#office">
<mapname="office">
<areashape="rect"coords="0,0,150,100"href="/lobby"alt="Lobby">
<areashape="circle"coords="200,150,40"href="/meeting-room"alt="Meeting room">
<areashape="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
<imgsrc="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
<imgsrc="cat.jpg"alt="A cat sitting on a windowsill"width="225"height="100">
❌ Invalid: using percentage in the attribute value
<iframesrc="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>
<iframesrc="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:
<iframesrc="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>
<iframesrc="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>
<codelang="yaml">
name: my-project
version: 1.0.0
</code>
</pre>
✅ Valid: using class for code language
<pre>
<codeclass="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 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"
<labelfor="zip">ZIP Code</label>
<inputtype="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)
<labelfor="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">
<spanid="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
<labelfor="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 -->
<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]>
<linkrel="stylesheet"href="ie.css">
<![endif]>
<!-- ✅ Remove legacy conditional comments -->
<linkrel="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>
<htmllang="fr">
<head>
<title>Café résumé</title>
<metacharset="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>
<htmllang="fr">
<head>
<metacharset="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.
Changing the character encoding declaration too late in the document prevents the browser from processing it correctly. The <meta charset> declaration must appear within the first 1024 bytes of the HTML document, and specifically before any non-ASCII content.
When a browser parses an HTML document, it reads the bytes as a stream. If it encounters a <meta charset> tag after it has already started interpreting content, it would need to go back and re-parse everything from the beginning — this is "non-streamable behavior." To avoid this, the HTML specification requires that the charset declaration appear very early in the document.
The most common causes of this error are:
- Placing
<meta charset>after other large<meta>tags, long<title>content, or<script>blocks in the<head>. - Placing
<meta charset>after content that pushes it beyond the 1024-byte boundary. - Including it in the
<body>instead of the<head>.
The fix is simple: make <meta charset="utf-8"> the very first element inside <head>, before any other elements.
Incorrect Example
<!DOCTYPE html>
<htmllang="en">
<head>
<title>A very long title that takes up many bytes and pushes the charset declaration further down in the document stream...</title>
<metaname="description"content="A very long description with lots of text that consumes bytes before the charset is declared...">
<metacharset="utf-8">
</head>
<body>
<p>Hello world</p>
</body>
</html>
Corrected Example
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>A very long title that takes up many bytes...</title>
<metaname="description"content="A very long description with lots of text...">
</head>
<body>
<p>Hello world</p>
</body>
</html>
Always keep <meta charset="utf-8"> as the first child of <head>. This ensures the browser knows the encoding before it processes any other content.
What Are Control Characters?
Control characters occupy code points U+0000 through U+001F and U+007F through U+009F in Unicode. They were originally designed for controlling hardware devices (e.g., U+0002 is "Start of Text," U+0007 is "Bell," U+001B is "Escape"). These characters have no visual representation and carry no semantic meaning in a web document.
The HTML specification explicitly forbids character references that resolve to most control characters. Even though the syntax  is a structurally valid character reference, the character it points to is not a permissible content character. The W3C validator raises this error to flag references like �, , , , and others that fall within the control character ranges.
Why This Is a Problem
- Standards compliance: The WHATWG HTML Living Standard defines a specific set of "noncharacter" and "control character" code points that must not be referenced. Using them produces a parse error.
- Unpredictable rendering: Browsers handle illegal control characters inconsistently. Some may silently discard them, others may render a replacement character (�), and others may exhibit unexpected behavior.
- Accessibility: Screen readers and other assistive technologies may choke on or misinterpret control characters, degrading the experience for users who rely on these tools.
- Data integrity: Control characters in your markup often indicate a copy-paste error, a corrupted data source, or a templating bug that inserts raw binary data into HTML output.
How to Fix It
- Identify the offending reference — look for character references like
,,�,, or similar that point to control character code points. - Determine intent — figure out what character or content was actually intended. Often, a control character reference is the result of a bug in a data pipeline or template engine.
- Remove or replace — either delete the reference entirely or replace it with the correct printable character or HTML entity.
Examples
Incorrect: Control character reference
This markup contains , which expands to the control character U+0002 (Start of Text) and triggers the validation error:
<p>Some text  more text</p>
Incorrect: Hexadecimal form of a control character
The same problem occurs with the hexadecimal syntax:
<p>Data: </p>
Correct: Remove the control character reference
If the control character was unintentional, simply remove it:
<p>Some text more text</p>
Correct: Use a valid character reference instead
If you intended to display a special character, use the correct printable code point or named entity. For example, to display a bullet (•), copyright sign (©), or ampersand (&):
<p>Item • Details</p>
<p>Copyright © 2024</p>
<p>Tom & Jerry</p>
Correct: Full document without control characters
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Example Page</title>
</head>
<body>
<p>This paragraph uses only valid character references: &<>©</p>
</body>
</html>
Common Control Character Code Points to Avoid
| Reference | Code Point | Name |
|---|---|---|
� | U+0000 | Null |
 | U+0001 | Start of Heading |
 | U+0002 | Start of Text |
 | U+0007 | Bell |
 | U+0008 | Backspace |
 | U+000B | Vertical Tab |
 | U+000C | Form Feed |
 | U+007F | Delete |
If your content is generated dynamically (from a database, API, or user input), sanitize the data before inserting it into HTML to strip out control characters. Most server-side languages and templating engines provide utilities for this purpose.
Character references are how HTML represents special characters that would otherwise be interpreted as markup or that aren't easily typed on a keyboard. They come in three forms:
- Named references like
&,<,© - Decimal numeric references like
<,© - Hexadecimal numeric references like
<,©
All three forms share the same structure: they begin with & and must end with ;. When you omit the trailing semicolon, the HTML parser enters error recovery mode. Depending on the context, it may still resolve the reference (browsers are lenient), but this behavior is not guaranteed and varies across situations. For example, © without a semicolon might still render as ©, but ¬it could be misinterpreted as the ¬ (¬) reference followed by it, producing unexpected output like "¬it" instead of the literal text "¬it".
Why this matters
- Unpredictable rendering: Without the semicolon, browsers use heuristic error recovery that can produce different results depending on surrounding text. What looks fine today might break with different adjacent characters.
- Standards compliance: The WHATWG HTML specification requires the semicolon terminator. Omitting it is a parse error.
- Maintainability: Other developers (or future you) may not realize the ampersand was intended as a character reference, making the code harder to read and maintain.
- Data integrity: In URLs within
hrefattributes, a missing semicolon on a character reference can corrupt query parameters and produce broken links.
How to fix it
- Add the missing semicolon to the end of every character reference.
- If you meant a literal ampersand, use
&instead of a bare&. This is especially common in URLs with query strings. - Search your document for patterns like
&somethingwithout a trailing;to catch all instances.
Examples
❌ Missing semicolon on named references
<p>5 < 10 and 10 > 5</p>
<p>© 2024 All rights reserved</p>
✅ Properly terminated named references
<p>5 < 10 and 10 > 5</p>
<p>© 2024 All rights reserved</p>
❌ Missing semicolon on numeric references
<p>The letter A: A</p>
<p>Hex example: A</p>
✅ Properly terminated numeric references
<p>The letter A: A</p>
<p>Hex example: A</p>
❌ Bare ampersand in a URL (common mistake)
<ahref="https://example.com/search?name=alice&age=30">Search</a>
Here the validator sees &age and tries to interpret it as a character reference without a semicolon.
✅ Escaped ampersand in a URL
<ahref="https://example.com/search?name=alice&age=30">Search</a>
❌ Ambiguous reference causing wrong output
<p>The entity ¬it; doesn't exist, but ¬ without a semicolon resolves to ¬</p>
✅ Use & when you want a literal ampersand
<p>The text &notit is displayed literally when properly escaped.</p>
A quick rule of thumb: every & in your HTML should either be the start of a complete, semicolon-terminated character reference, or it should itself be written as &.
When you use the W3C Markup Validation Service by submitting a URL (rather than uploading a file or pasting code directly), the validator attempts to fetch the page from your server over the internet. If the server doesn't respond within a set period, the connection times out and the validator reports this error instead of any HTML validation results.
This issue is entirely network- or server-related and has nothing to do with the quality of your HTML markup. However, it prevents you from validating your code, so it's worth resolving or working around.
Common Causes
There are several reasons the validator may fail to connect:
- Server is offline or unresponsive. The web server hosting your site may be down, overloaded, or restarting.
- Firewall or security rules blocking the validator. Some server configurations, Web Application Firewalls (WAFs), or hosting providers block automated requests. The W3C Validator identifies itself via its
User-Agentheader, and some security tools may reject it. - The URL is not publicly accessible. If your site is on
localhost, behind a VPN, on an intranet, or restricted by IP allowlisting, the validator cannot reach it. - DNS issues. The domain name may not resolve correctly from the validator's network, even if it works from your machine.
- SSL/TLS misconfiguration. If the site uses HTTPS but has an expired certificate, a self-signed certificate, or an incomplete certificate chain, the connection may fail or be refused.
- Slow server response. If your page takes a very long time to generate (e.g., a complex database query), the validator may time out before receiving a response.
- Cloudflare or CDN challenge pages. Services like Cloudflare may present a bot-detection challenge or CAPTCHA to the validator, preventing it from fetching the actual page.
How to Fix It
1. Verify your site is publicly reachable
Test that your URL is accessible from outside your local network. You can use tools like curl from a remote server or an online service like "Down For Everyone Or Just Me."
curl -I https://example.com
If this returns an HTTP status code like 200 OK, the server is responding.
2. Check your firewall and security rules
Make sure your server or hosting provider isn't blocking the W3C Validator's requests. The validator's User-Agent string typically contains W3C_Validator. If you use a WAF or bot-protection service, add an exception for the validator.
3. Fix SSL/TLS issues
If your site uses HTTPS, verify your certificate is valid and the chain is complete. You can test this with tools like SSL Labs.
4. Use an alternative validation method
If you can't make your site publicly accessible to the validator (e.g., it's a staging server or a local development environment), you can bypass the network requirement entirely:
- Direct input: Copy your page's HTML source and paste it into the validator's "Validate by Direct Input" tab at validator.w3.org.
- File upload: Save the HTML file locally and use the "Validate by File Upload" tab.
- View source, then paste: In your browser, view the page source (
Ctrl+UorCmd+U), copy the full HTML, and paste it into the validator.
5. Reduce server response time
If your server is online but slow, optimize the page so it responds faster. The validator expects a response within a reasonable timeout window. Consider caching, reducing database queries, or simplifying server-side processing for the page you're trying to validate.
Examples
Validating a local development site (will fail)
Submitting a URL like this to the W3C Validator will time out because the validator cannot access your local machine:
http://localhost:3000/index.html
http://192.168.1.50/mysite/
http://my-dev-machine.local/page.html
Workaround: Validate by direct input
Instead, copy your HTML and paste it directly. For example, if your page contains:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<h1>Hello, world!</h1>
<p>This is my page.</p>
</body>
</html>
Paste this into the "Validate by Direct Input" field on the W3C Validator. This completely avoids the network connection and lets you validate your markup regardless of server accessibility.
The lang attribute on the <html> element sets the default language for all text content within the page. Without it, assistive technologies like screen readers have to guess which language the content is in, which can lead to garbled or incorrectly pronounced text. For example, a French screen reader attempting to read English text — or vice versa — produces a poor experience for users who rely on these tools.
Beyond accessibility, the lang attribute matters for several other reasons:
- Search engines use it to serve the correct language version of your page in search results.
- Browsers rely on it to choose appropriate fonts, hyphenation rules, and quotation mark styles.
- Translation tools use it to detect the source language of the page.
- CSS selectors like
:lang()depend on it to apply language-specific styling.
The value of the lang attribute must be a valid BCP 47 language tag. Common examples include en (English), fr (French), es (Spanish), de (German), zh (Chinese), ja (Japanese), and ar (Arabic). You can also be more specific with region subtags, such as en-US for American English or pt-BR for Brazilian Portuguese.
If your page contains sections in a different language than the primary one, you can use the lang attribute on individual elements to override the document-level language for that section.
Examples
Missing lang attribute (triggers the warning)
<!DOCTYPE html>
<html>
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Fixed with lang attribute
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Using a region subtag for specificity
<!DOCTYPE html>
<htmllang="en-GB">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Colour is spelt differently here.</p>
</body>
</html>
Overriding the language for a specific section
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>Multilingual Page</title>
</head>
<body>
<p>This paragraph is in English.</p>
<plang="fr">Ce paragraphe est en français.</p>
</body>
</html>
In this last example, the document language is English, but the second paragraph is marked as French. A screen reader will switch to French pronunciation rules for that paragraph, then revert to English for the rest of the page.
When you set user-scalable=no in your viewport meta tag, the browser completely disables pinch-to-zoom and other scaling gestures on mobile devices. Similarly, setting maximum-scale=1 (or any low value) caps how far a user can zoom in, effectively locking them out of enlarging content. While developers sometimes use these values to create an "app-like" experience or prevent layout issues during zoom, they directly violate accessibility best practices.
Why this is a problem
Accessibility
The Web Content Accessibility Guidelines (WCAG) Success Criterion 1.4.4 (Resize Text) requires that text can be resized up to 200% without loss of content or functionality. Preventing zoom makes it impossible for users with low vision, cognitive disabilities, or motor impairments to interact comfortably with your page. Many users depend on pinch-to-zoom as their primary way to read content on mobile devices.
Standards compliance
The W3C HTML Validator flags this as a warning because it conflicts with established accessibility standards. While it won't cause your page to fail validation outright, it signals a practice that harms usability. Modern browsers and operating systems have also started to override restrictive viewport settings in some cases — for example, iOS Safari ignores user-scalable=no by default — which means the restriction may not even work as intended while still triggering warnings.
User experience
Even for users without disabilities, preventing zoom can be frustrating. Small text, dense layouts, or content that doesn't quite fit a screen size can all benefit from the user being able to zoom in. Restricting this capability removes a fundamental browser feature that users expect.
How to fix it
- Remove
user-scalable=nofrom your viewport meta tag. If present, either delete it or set it toyes. - Remove or increase
maximum-scale. If you need to set it, use a value of5or higher. Ideally, remove it entirely and let the browser handle zoom limits. - Remove
minimum-scaleif it's set to1, as this can also restrict zoom behavior on some browsers when combined with other values. - Test your layout at various zoom levels to ensure content reflows properly and remains usable.
Examples
❌ Viewport that prevents zooming
<metaname="viewport"content="width=device-width, initial-scale=1, user-scalable=no">
This completely disables user zoom on supporting browsers.
❌ Viewport with restrictive maximum-scale
<metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=1.0">
This caps zoom at 100%, effectively preventing any meaningful zoom.
❌ Both restrictions combined
<metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
This is the most restrictive combination and is commonly seen in mobile-first frameworks and templates.
✅ Accessible viewport (recommended)
<metaname="viewport"content="width=device-width, initial-scale=1">
This sets a responsive viewport without restricting zoom at all. The browser's default zoom behavior is preserved, and users can scale freely.
✅ Accessible viewport with a generous maximum-scale
<metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=5">
If you have a specific reason to set maximum-scale, use a value of 5 or higher. This still allows substantial zoom while giving you some control over extreme zoom levels.
✅ Full document example
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<metaname="viewport"content="width=device-width, initial-scale=1">
<title>Accessible Page</title>
</head>
<body>
<h1>Welcome</h1>
<p>This page allows users to zoom freely.</p>
</body>
</html>
If your layout breaks when users zoom in, the solution is to fix the CSS — using relative units like em, rem, or percentages, and responsive design techniques — rather than disabling zoom. A well-built responsive layout should handle zoom gracefully without needing to restrict it.
The HTML heading elements <h1> through <h6> define a document's heading hierarchy. The <h1> element represents the highest-level heading, and each subsequent level (<h2>, <h3>, etc.) represents a deeper subsection. This hierarchy is critical for both accessibility and document structure.
The HTML5 specification once introduced a "document outline algorithm" that would have allowed multiple <h1> elements to be automatically scoped by their parent sectioning elements (<section>, <article>, <nav>, <aside>). Under this model, an <h1> inside a nested <section> would be treated as a lower-level heading. However, no browser or assistive technology ever implemented this algorithm. The outline algorithm was eventually removed from the WHATWG HTML specification. In practice, screen readers and other tools treat every <h1> on a page as a top-level heading, regardless of nesting.
This matters for several reasons:
- Accessibility: Screen reader users frequently navigate by headings to get an overview of a page's content. When multiple
<h1>elements exist, the heading list becomes flat and unclear, making it difficult to understand the page's structure and find specific content. - SEO: Search engines use heading hierarchy to understand page structure and content importance. Multiple
<h1>elements can dilute the semantic signal of your primary page topic. - Standards compliance: While using multiple
<h1>elements is not a validation error, the W3C validator raises this as a warning because it is widely considered a best practice to reserve<h1>for the single, top-level page heading.
To fix this warning, follow these guidelines:
- Use exactly one
<h1>per page to describe the main topic or title. - Use
<h2>for major sections beneath it,<h3>for subsections within those, and so on. - Don't skip heading levels (e.g., jumping from
<h1>to<h3>without an<h2>).
Examples
Incorrect: Multiple <h1> elements
This example uses <h1> inside each sectioning element, which triggers the warning. Screen readers will present all three headings at the same level, losing the intended hierarchy.
<h1>My Blog</h1>
<section>
<h1>Latest Posts</h1>
<article>
<h1>How to Write Accessible HTML</h1>
<p>Writing semantic HTML is important for accessibility.</p>
</article>
<article>
<h1>Understanding CSS Grid</h1>
<p>CSS Grid makes complex layouts straightforward.</p>
</article>
</section>
Correct: Proper heading hierarchy
Use a single <h1> for the page title and nest subsequent headings using the appropriate levels.
<h1>My Blog</h1>
<section>
<h2>Latest Posts</h2>
<article>
<h3>How to Write Accessible HTML</h3>
<p>Writing semantic HTML is important for accessibility.</p>
</article>
<article>
<h3>Understanding CSS Grid</h3>
<p>CSS Grid makes complex layouts straightforward.</p>
</article>
</section>
Incorrect: <h1> nested inside a section without a parent heading
Even a single <h1> nested deeply inside sectioning content can trigger this warning if the structure suggests it is not the page's primary heading.
<sectionclass="about">
<article>
<h1>Article heading</h1>
<p>Lorem ipsum dolor sit amet.</p>
</article>
</section>
Correct: Section with its own heading and properly ranked article heading
<sectionclass="about">
<h1>About</h1>
<article>
<h2>Article heading</h2>
<p>Lorem ipsum dolor sit amet.</p>
</article>
</section>
Correct: Full page structure with clear heading hierarchy
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>Company Homepage</title>
</head>
<body>
<header>
<h1>Acme Corporation</h1>
</header>
<main>
<section>
<h2>Our Services</h2>
<h3>Web Development</h3>
<p>We build modern, accessible websites.</p>
<h3>Design</h3>
<p>Our design team creates beautiful interfaces.</p>
</section>
<section>
<h2>About Us</h2>
<p>We have been in business since 2005.</p>
</section>
</main>
</body>
</html>
In this structure, screen readers will present a clear, navigable outline: one top-level heading followed by properly nested subheadings that reflect the logical organization of the content.
Multiple h1 elements on a page can confuse screen readers and other assistive tools, which treat every h1 as the top-level heading.
HTML headings (h1 through h6) form an outline of your document. The h1 element represents the highest-level heading, and most accessibility guidelines recommend using only one h1 per page. When screen readers encounter multiple h1 elements, they may present them all as equally important top-level sections, making it harder for users to understand the page structure.
Instead of using multiple h1 elements, use a proper heading hierarchy. Start with a single h1 for the main topic of the page, then use h2 for major sections, h3 for subsections, and so on. This creates a clear, navigable document outline.
The W3C warning also mentions a headingoffset attribute, which is a proposed feature for <section> elements that would allow automatic heading level adjustment. However, this attribute is not yet implemented in any browser, so you should not rely on it.
Example with the issue
<body>
<h1>My Website</h1>
<section>
<h1>About Us</h1>
<p>Some content here.</p>
</section>
<section>
<h1>Contact</h1>
<p>More content here.</p>
</section>
</body>
Example with proper heading hierarchy
<body>
<h1>My Website</h1>
<section>
<h2>About Us</h2>
<p>Some content here.</p>
</section>
<section>
<h2>Contact</h2>
<p>More content here.</p>
</section>
</body>
Keep one h1 per page and nest subsequent headings using h2 through h6 to reflect the logical structure of your content. This approach is well-supported across all browsers and assistive technologies today.
Content Security Policy (CSP) is a security mechanism that lets you control which resources a browser is allowed to load for your page. When defined via a <meta http-equiv="Content-Security-Policy"> tag, the validator checks whether the content attribute contains a well-formed policy. If the policy string contains unrecognized directives, malformed source expressions, or syntax errors, the validator reports "Bad content security policy."
Common causes of this error include:
- Misspelled directive names — e.g.,
script-scrinstead ofscript-src. - Invalid source values — e.g., using
selfwithout single quotes (it must be'self'). - Using directives not allowed in
<meta>tags — theframe-ancestors,report-uri, andsandboxdirectives are not supported when CSP is delivered via a<meta>element. - Incorrect separators — directives are separated by semicolons (
;), not commas or pipes. - Missing or extra quotes — keywords like
'none','self','unsafe-inline', and'unsafe-eval'must be wrapped in single quotes. Conversely, hostnames and URLs must not be quoted.
This matters because a malformed CSP may be silently ignored by browsers, leaving your site without the intended protection against cross-site scripting (XSS) and data injection attacks. Even a small typo can cause an entire directive to be skipped, creating a security gap you might not notice.
How to fix it
- Check directive names against the CSP specification. Valid fetch directives include
default-src,script-src,style-src,img-src,font-src,connect-src,media-src,object-src,child-src,worker-src, and others. - Wrap keyword values in single quotes:
'self','none','unsafe-inline','unsafe-eval', and nonce/hash sources like'nonce-abc123'. - Separate directives with semicolons. Multiple source values within a single directive are separated by spaces.
- Avoid directives that are invalid in
<meta>tags. If you needframe-ancestorsorreport-uri, deliver CSP via an HTTP header instead. - Don't include the header name inside the
contentattribute. Thecontentvalue should contain only the policy itself.
Examples
❌ Misspelled directive and unquoted keyword
<metahttp-equiv="Content-Security-Policy"
content="default-src self; script-scr https://example.com">
Here, self is missing its required single quotes, and script-scr is a typo for script-src.
✅ Corrected directive name and properly quoted keyword
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self'; script-src https://example.com">
❌ Using a directive not allowed in a <meta> tag
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self'; frame-ancestors 'none'">
The frame-ancestors directive is ignored in <meta> elements and may trigger a validation warning.
✅ Removing the unsupported directive from the <meta> tag
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self'">
Deliver frame-ancestors via an HTTP response header on your server instead.
❌ Using commas instead of semicolons between directives
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self', script-src 'none', style-src 'self'">
✅ Using semicolons to separate directives
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'none'; style-src 'self'">
❌ Quoting a hostname (hostnames must not be in quotes)
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self'; img-src 'https://images.example.com'">
✅ Hostname without quotes
<metahttp-equiv="Content-Security-Policy"
content="default-src 'self'; img-src https://images.example.com">
When in doubt, use an online CSP evaluator to validate your policy string before adding it to your HTML. This ensures both syntactic correctness and that the policy actually enforces what you intend.
The align-items property controls how flex or grid items are aligned along the cross axis of their container. While many CSS properties accept auto as a value, align-items is not one of them. The CSS specification defines a specific set of accepted values, and using auto will cause the declaration to be ignored by browsers, potentially breaking your intended layout.
This mistake often stems from confusion with the related property align-self, which does accept auto as its default value. When align-self is set to auto, it defers to the parent container's align-items value. However, align-items itself has no such delegation mechanism — it is the property that sets the default alignment for all items in the container.
The valid values for align-items include:
normal— behaves asstretchin flex containers and has context-dependent behavior in other layout modes.stretch— items are stretched to fill the container along the cross axis (the default behavior in flexbox).center— items are centered along the cross axis.flex-start/start— items are aligned to the start of the cross axis.flex-end/end— items are aligned to the end of the cross axis.baseline/first baseline/last baseline— items are aligned based on their text baselines.self-start/self-end— items are aligned based on their own writing mode.
If you intended the default behavior, use stretch (for flexbox) or normal. If you were trying to reset the property, use initial, unset, or revert instead of auto.
Examples
Incorrect: using auto as a value
<divstyle="display: flex;align-items: auto;">
<p>Item one</p>
<p>Item two</p>
</div>
This triggers the validation error because auto is not a recognized value for align-items.
Fixed: using stretch for default flexbox behavior
<divstyle="display: flex;align-items: stretch;">
<p>Item one</p>
<p>Item two</p>
</div>
Fixed: using center to center items
<divstyle="display: flex;align-items: center;">
<p>Item one</p>
<p>Item two</p>
</div>
Fixed: using flex-start to align items to the top
<divstyle="display: flex;align-items: flex-start;">
<p>Item one</p>
<p>Item two</p>
</div>
Correct use of auto with align-self
If your intention was to let a specific child item defer to its parent's alignment, use align-self: auto on the child element instead:
<divstyle="display: flex;align-items: center;">
<p>Centered item</p>
<pstyle="align-self: auto;">Also centered (defers to parent)</p>
<pstyle="align-self: flex-end;">Aligned to the end</p>
</div>
Here, align-self: auto is valid on individual items and tells them to inherit the align-items value from the container.
left is not a valid value for the align-items CSS property.
The align-items property controls how flex or grid items are aligned along the cross axis of their container. Its valid values include stretch, flex-start, flex-end, center, baseline, start, end, self-start, and self-end.
The value left is not recognized because align-items works on the cross axis (typically vertical), not the inline/horizontal axis. If you want to align items to the start, use flex-start or start instead.
If you're actually trying to align content horizontally to the left, you likely want the justify-content property (which controls the main axis) or text-align: left on the container.
How to Fix
Incorrect:
<divstyle="display: flex;align-items: left;">
<p>Hello</p>
</div>
Fixed — aligning items to the start of the cross axis:
<divstyle="display: flex;align-items: flex-start;">
<p>Hello</p>
</div>
Fixed — aligning items horizontally to the left (main axis):
<divstyle="display: flex;justify-content: flex-start;">
<p>Hello</p>
</div>
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