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 sizes attribute works together with the srcset attribute to help the browser choose the most appropriate image source before the page layout is calculated. It describes how wide the image will be displayed under various viewport conditions, allowing the browser to pick an optimally sized image from the candidates listed in srcset. According to the HTML specification, the value of sizes must be a valid source size list — a comma-separated set of media conditions paired with lengths, with a default length at the end. An empty string ("") does not satisfy this requirement and is therefore invalid.
When the browser encounters an empty sizes attribute, it cannot determine the intended display width of the image. This defeats the purpose of responsive images and may cause the browser to fall back to a default behavior (typically assuming 100vw), which could result in downloading an unnecessarily large image. Beyond the functional issue, an empty attribute signals a code quality problem — it often means a template is outputting the attribute even when no value has been configured.
Why this matters
- Standards compliance: The HTML specification explicitly requires
sizesto be a non-empty, valid source size list when present. An empty value is a parse error. - Performance: Without a proper
sizesvalue, the browser cannot make an informed decision about whichsrcsetcandidate to download. This can lead to wasted bandwidth and slower page loads, especially on mobile devices. - Code quality: Empty attributes clutter your markup and suggest missing configuration, making the code harder to maintain.
How to fix it
- Provide a valid
sizesvalue that describes how wide the image will actually render at different viewport widths. - Remove
sizesentirely if you are not using width descriptors (w) insrcset. Whensrcsetuses pixel density descriptors (1x,2x), thesizesattribute is not needed. - Remove both
sizesandsrcsetif you don't need responsive image selection at all.
Examples
❌ Empty sizes attribute
<img
src="photo.jpg"
srcset="photo-small.jpg 480w, photo-large.jpg 1200w"
sizes=""
alt="A mountain landscape">
The empty sizes="" triggers the validation error.
✅ Valid sizes with a single default value
If the image always takes up the full viewport width:
<img
src="photo.jpg"
srcset="photo-small.jpg 480w, photo-large.jpg 1200w"
sizes="100vw"
alt="A mountain landscape">
✅ Valid sizes with media conditions
If the image displays at different widths depending on the viewport:
<img
src="photo.jpg"
srcset="photo-small.jpg 480w, photo-medium.jpg 800w, photo-large.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1024px) 50vw, 33vw"
alt="A mountain landscape">
This tells the browser: use 100vw on viewports up to 600px, 50vw up to 1024px, and 33vw otherwise.
✅ Removing sizes when using density descriptors
When srcset uses density descriptors instead of width descriptors, sizes is not applicable and should be omitted:
<img
src="photo.jpg"
srcset="photo.jpg 1x, photo-2x.jpg 2x"
alt="A mountain landscape">
✅ Removing both attributes when not needed
If responsive image selection isn't required, simply use a standard <img>:
<imgsrc="photo.jpg"alt="A mountain landscape">
Common template fix
If your CMS or templating system conditionally outputs these attributes, ensure the sizes attribute is only rendered when it has a value. For example, in a template:
<!-- Before (always outputs sizes, even when empty) -->
<imgsrc="photo.jpg"srcset="{{srcset}}"sizes="{{sizes}}"alt="{{alt}}">
<!-- After (only outputs sizes when it has a value) -->
<imgsrc="photo.jpg"srcset="{{srcset}}"{{#ifsizes}}sizes="{{sizes}}"{{/if}} alt="{{alt}}">
The HTML specification requires that if the name attribute is used on a <form> element, its value must be a non-empty string. An empty name="" attribute serves no practical purpose — it doesn't register the form in the document.forms named collection, and it can't be used as a valid reference in scripts. The W3C validator flags this as an error because it violates the content model defined in the WHATWG HTML Living Standard.
The name attribute on a form is primarily used to access the form programmatically through document.forms["formName"]. When the value is empty, this lookup mechanism doesn't work, so the attribute becomes meaningless. This is different from the id attribute, which also identifies elements but participates in fragment navigation and CSS targeting. The name attribute on <form> is specifically for the legacy document.forms named getter interface.
It's worth noting that the name attribute value on a form must not equal an empty string, and it should be unique among the form elements in the forms collection. While duplicate names won't cause a validation error, they can lead to unexpected behavior when accessing forms by name in JavaScript.
How to fix
You have two options:
- Remove the attribute entirely. If you're not referencing the form by name in JavaScript, the
nameattribute is unnecessary. You can useidinstead for CSS or JavaScript targeting. - Provide a meaningful, non-empty value. If you need to reference the form through
document.forms, give it a descriptive name that reflects its purpose.
Examples
❌ Invalid: empty name attribute
<formname="">
<labelfor="email">Email</label>
<inputtype="email"id="email"name="email">
<buttontype="submit">Subscribe</button>
</form>
This triggers the validator error because the name attribute is present but has an empty string value.
✅ Fixed: attribute removed
<form>
<labelfor="email">Email</label>
<inputtype="email"id="email"name="email">
<buttontype="submit">Subscribe</button>
</form>
If you don't need to reference the form by name, simply remove the attribute.
✅ Fixed: meaningful name provided
<formname="subscriptionForm">
<labelfor="email">Email</label>
<inputtype="email"id="email"name="email">
<buttontype="submit">Subscribe</button>
</form>
With a non-empty name, you can now access the form in JavaScript using document.forms["subscriptionForm"] or document.forms.subscriptionForm.
✅ Alternative: using id instead
<formid="subscription-form">
<labelfor="email">Email</label>
<inputtype="email"id="email"name="email">
<buttontype="submit">Subscribe</button>
</form>
In modern development, using id with document.getElementById() or document.querySelector() is often preferred over the name attribute for form identification. The name attribute on <form> is a legacy feature that remains valid but isn't required for most use cases.
The imagesizes attribute is used exclusively on <link> elements that have rel="preload" and as="image". It works in tandem with the imagesrcset attribute to allow the browser to preload the most appropriate image from a set of candidates — mirroring how sizes and srcset work on an <img> element. When the browser encounters these attributes on a <link>, it can begin fetching the right image resource early, before it even parses the <img> tag in the document body.
When imagesizes is set to an empty string (""), the browser has no information about the intended display size of the image, which defeats the purpose of responsive image preloading. The browser cannot select the best candidate from imagesrcset without knowing how large the image will be rendered. An empty value is invalid per the HTML specification, which requires the attribute to contain a valid source size list (the same syntax used by the sizes attribute on <img>).
This matters for both performance and standards compliance. Responsive preloading is a performance optimization — an empty imagesizes undermines that by leaving the browser unable to make an informed choice. From a standards perspective, the validator correctly rejects the empty value because the attribute's defined value space does not include the empty string.
How to fix it
- Provide a valid sizes value that matches the
sizesattribute on the corresponding<img>element in your page. This tells the browser how wide the image will be at various viewport widths. - Remove
imagesizesentirely if you don't need responsive preloading. If you're preloading a single image (usinghrefinstead ofimagesrcset), you don't needimagesizesat all.
Examples
❌ Bad: empty imagesizes attribute
<link
rel="preload"
as="image"
imagesrcset="hero-480.jpg 480w, hero-800.jpg 800w, hero-1200.jpg 1200w"
imagesizes="">
The empty imagesizes="" triggers the validation error and prevents the browser from selecting the correct image candidate.
✅ Fixed: providing a valid sizes value
<link
rel="preload"
as="image"
imagesrcset="hero-480.jpg 480w, hero-800.jpg 800w, hero-1200.jpg 1200w"
imagesizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px">
The imagesizes value uses the same syntax as the sizes attribute on <img>. It provides media conditions paired with lengths, with a fallback length at the end. This value should match the sizes attribute on the corresponding <img> element in your markup.
✅ Fixed: simple full-width image
<link
rel="preload"
as="image"
imagesrcset="banner-640.jpg 640w, banner-1280.jpg 1280w"
imagesizes="100vw">
If the image spans the full viewport width, 100vw is a straightforward and valid value.
✅ Fixed: removing the attribute when not needed
<linkrel="preload"as="image"href="logo.png">
If you're preloading a single, non-responsive image, omit both imagesrcset and imagesizes and use the href attribute instead. The imagesizes attribute is only meaningful when paired with imagesrcset.
The autocomplete attribute tells the browser whether and how it should autofill a form field. The HTML living standard defines a specific set of allowed values — an empty string is not among them. When the attribute is present but empty, the browser receives ambiguous instructions: you've explicitly declared the attribute, signaling intent, but provided no actual directive. Different browsers may interpret this inconsistently, some treating it as on, others ignoring it, and others falling back to default behavior.
This matters for several reasons:
- Standards compliance: The WHATWG HTML specification requires
autocompleteto contain either the keywordsonoroff, or one or more valid autofill detail tokens. An empty string satisfies none of these. - Accessibility: Autofill helps users with motor impairments or cognitive disabilities complete forms more quickly and accurately. An ambiguous
autocompletevalue can interfere with assistive technologies that rely on these hints. - User experience: Specific autofill tokens like
email,tel,street-address, andcurrent-passwordallow browsers and password managers to suggest the right data for the right field. Using them correctly makes forms faster and easier to complete.
This issue commonly arises when a framework or template engine outputs autocomplete="" as a default, or when a developer intends to disable autocomplete but leaves the value blank instead of using off.
How to fix it
Choose one of these approaches depending on your intent:
- Remove the attribute if you want default browser behavior (the browser decides whether to autofill).
- Use
onto explicitly allow autofill. - Use
offto explicitly discourage autofill (note: browsers may still autofill for login fields regardless). - Use a specific autofill token to tell the browser exactly what kind of data the field expects. This is the most helpful option for users.
Common autofill tokens include: name, given-name, family-name, email, username, new-password, current-password, tel, street-address, postal-code, country, and cc-number. You can find the full list in the WHATWG autofill specification.
Examples
Incorrect: empty autocomplete value
<inputtype="text"name="username"autocomplete="">
This triggers the validation error because the attribute is present but contains no valid token.
Correct: remove the attribute entirely
If you have no specific autofill preference, simply omit the attribute:
<inputtype="text"name="username">
Correct: use on or off
Explicitly enable or disable autofill:
<inputtype="text"name="username"autocomplete="on">
<inputtype="text"name="search-query"autocomplete="off">
Correct: use specific autofill tokens
Specific tokens give browsers the best hints for filling in the right data. This is the recommended approach for forms that collect personal information:
<form>
<labelfor="name">Full name</label>
<inputtype="text"id="name"name="name"autocomplete="name">
<labelfor="useremail">Email</label>
<inputtype="email"id="useremail"name="useremail"autocomplete="email">
<labelfor="phone">Phone</label>
<inputtype="tel"id="phone"name="phone"autocomplete="tel">
<labelfor="pwd">Password</label>
<inputtype="password"id="pwd"name="pwd"autocomplete="current-password">
<buttontype="submit">Sign in</button>
</form>
Using precise tokens like current-password and email helps password managers and mobile keyboards provide the most relevant suggestions, improving the experience for all users.
The charset attribute on the <script> element tells the browser what character encoding to use when interpreting the referenced external script file. When a script is written directly inside the HTML document (an inline script), the script's character encoding is inherently the same as the document's encoding — there is no separate file to decode. Because of this, the HTML specification requires that charset only appear on <script> elements that also have a src attribute pointing to an external file.
Including charset without src violates the HTML specification and signals a misunderstanding of how character encoding works for inline scripts. Validators flag this because browsers ignore the charset attribute on inline scripts, which means it has no effect and could mislead developers into thinking they've set the encoding when they haven't.
It's also worth noting that the charset attribute on <script> is deprecated in the HTML living standard, even for external scripts. The modern best practice is to serve external script files with the correct Content-Type HTTP header (e.g., Content-Type: application/javascript; charset=utf-8) or to simply ensure all your files use UTF-8 encoding, which is the default. If you're working with an older codebase that still uses charset, consider removing it entirely and relying on UTF-8 throughout.
Examples
Incorrect: charset on an inline script
This triggers the validation error because charset is specified without a corresponding src attribute.
<scriptcharset="utf-8">
console.log("Hello, world!");
</script>
Correct: Remove charset from inline scripts
Since inline scripts use the document's encoding, simply remove the charset attribute.
<script>
console.log("Hello, world!");
</script>
Correct: Use charset with an external script (deprecated but valid)
If you need to specify the encoding of an external script, both charset and src must be present. Note that this usage, while valid, is deprecated.
<scriptsrc="app.js"charset="utf-8"></script>
Recommended: External script without charset
The preferred modern approach is to omit charset entirely and ensure the server delivers the file with the correct encoding header, or simply use UTF-8 for everything.
<scriptsrc="app.js"></script>
The HTML <figure> element represents self-contained content — such as an image, diagram, code snippet, or quotation — optionally accompanied by a caption provided by a <figcaption> element. When a <figcaption> is present, the browser and assistive technologies already understand the relationship between the figure and its caption. This built-in semantic relationship is part of the ARIA in HTML specification, which governs how native HTML elements map to accessibility roles.
The role attribute is typically used to assign or override the ARIA role of an element for assistive technologies like screen readers. However, the ARIA in HTML specification explicitly restricts certain role assignments when an element's native semantics are already well-defined by its content. A <figure> with a <figcaption> is one such case — the presence of the caption establishes a clear semantic structure that the role attribute would interfere with.
This restriction exists for several important reasons:
- Accessibility conflicts: Adding
role="figure"is redundant since the element already has that implicit role. Adding a different role (likerole="img"orrole="group") could confuse assistive technologies by contradicting the semantic meaning established by the<figcaption>. Screen readers may ignore the caption or announce the element incorrectly. - Standards compliance: The ARIA in HTML specification states that when a
<figure>has a<figcaption>descendant, noroleattribute is allowed. The W3C validator enforces this rule. - Maintainability: Relying on native HTML semantics rather than ARIA overrides keeps your markup simpler and less error-prone. The first rule of ARIA is: "If you can use a native HTML element with the semantics and behavior you require already built in, do so."
To fix this issue, remove the role attribute from the <figure> element. The <figcaption> provides all the semantic context needed.
Examples
Incorrect: role attribute on <figure> with <figcaption>
Adding role="figure" is redundant and triggers the validation error:
<figurerole="figure">
<imgsrc="chart.png"alt="Sales data for Q3 2024">
<figcaption>Figure 1: Quarterly sales by region.</figcaption>
</figure>
Using a different role like role="img" also triggers the error and can cause accessibility problems:
<figurerole="img">
<imgsrc="chart.png"alt="Sales data for Q3 2024">
<figcaption>Figure 1: Quarterly sales by region.</figcaption>
</figure>
Correct: <figure> with <figcaption> and no role
Simply remove the role attribute. The <figure> and <figcaption> elements handle semantics on their own:
<figure>
<imgsrc="chart.png"alt="Sales data for Q3 2024">
<figcaption>Figure 1: Quarterly sales by region.</figcaption>
</figure>
Correct: <figure> without <figcaption> can use a role
If you have a <figure> without a <figcaption>, the restriction does not apply. In this case, you may use a role attribute if needed:
<figurerole="img"aria-label="Sales data for Q3 2024">
<imgsrc="chart.png"alt="">
</figure>
Correct: <figure> with <figcaption> containing other media
The fix applies regardless of the type of content inside the <figure>:
<figure>
<pre><code>const greeting = "Hello, world!";</code></pre>
<figcaption>A simple variable assignment in JavaScript.</figcaption>
</figure>
<figure>
<blockquote>
<p>The best way to predict the future is to invent it.</p>
</blockquote>
<figcaption>Alan Kay</figcaption>
</figure>
In every case, the <figcaption> provides the accessible name and descriptive context for the <figure>, making any role attribute unnecessary and non-conformant.
The defer and async boolean attributes control how and when an external script is fetched and executed relative to HTML parsing. These attributes exist specifically to optimize the loading of external resources. An inline <script> block (one without a src attribute) doesn't need to be "downloaded" — its content is already embedded in the HTML document. Because of this, the defer attribute has no meaningful effect on inline scripts, and the HTML specification explicitly forbids this combination.
According to the WHATWG HTML living standard, the defer attribute "must not be specified if the src attribute is not present." Browsers will simply ignore the defer attribute on inline scripts, which means the script will execute synchronously as if defer were never added. This can mislead developers into thinking their inline script execution is being deferred when it isn't, potentially causing subtle timing bugs that are difficult to diagnose.
The same rule applies to the async attribute — it also requires the presence of a src attribute to be valid.
How to fix it
You have two options depending on your situation:
- If the script should be deferred, move the inline code into an external
.jsfile and reference it with thesrcattribute alongsidedefer. - If the script must remain inline, remove the
deferattribute entirely. If you need deferred execution for inline code, consider placing the<script>element at the end of the<body>, or useDOMContentLoadedto wait for the document to finish parsing.
Examples
❌ Invalid: defer on an inline script
<scriptdefer>
console.log("hello");
</script>
This triggers the validation error because defer is present without a corresponding src attribute.
✅ Fix option 1: Add a src attribute
Move the JavaScript into an external file (e.g., app.js) and reference it:
<scriptdefersrc="app.js"></script>
The browser will download app.js in parallel with HTML parsing and execute it only after the document is fully parsed.
✅ Fix option 2: Remove defer from the inline script
If the script must stay inline, simply remove the defer attribute:
<script>
console.log("hello");
</script>
✅ Fix option 3: Use DOMContentLoaded for deferred inline execution
If you need your inline script to wait until the DOM is ready, wrap the code in a DOMContentLoaded event listener:
<script>
document.addEventListener("DOMContentLoaded",function(){
console.log("DOM is fully parsed");
});
</script>
This achieves a similar effect to defer but is valid for inline scripts.
❌ Invalid: async on an inline script
The same rule applies to async:
<scriptasync>
document.title="Updated";
</script>
✅ Fixed
<scriptasyncsrc="update-title.js"></script>
Or simply remove async if the script is inline:
<script>
document.title="Updated";
</script>
When an element has role="button", assistive technologies treat it as a single interactive control — just like a native <button>. Users expect to tab to it once, and then activate it with Enter or Space. If a focusable descendant (an element with tabindex) exists inside that button, it creates a second tab stop within what should be a single control. This breaks the expected interaction model and confuses both keyboard users and screen readers.
The WAI-ARIA specification explicitly states that certain roles, including button, must not contain interactive or focusable descendants. This is because a button is an atomic widget — it represents one action and should receive focus as a single unit. When a screen reader encounters a role="button" element, it announces it as a button and expects the user to interact with it directly. A nested focusable element disrupts this by creating an ambiguous focus target: should the user interact with the outer button or the inner focusable element?
This issue commonly arises when developers wrap inline elements like <span> or <a> with tabindex inside a <div role="button">, often to style parts of the button differently or to add click handlers. The correct approach is to ensure only the outermost button-like element is focusable.
How to fix it
Use a native
<button>element. This is always the best solution. Native buttons handle focus, keyboard interaction (Enter and Space key activation), and accessibility announcements automatically — noroleortabindexneeded.Move
tabindexto therole="button"container. If you must userole="button"(for example, when a<div>needs to behave as a button due to design constraints), placetabindex="0"on the container itself and removetabindexfrom all descendants.Remove
tabindexfrom descendants. If the inner element doesn't actually need to be independently focusable, simply remove thetabindexattribute from it.
When using role="button" on a non-interactive element, remember you also need to implement keyboard event handlers for Enter and Space to fully replicate native button behavior.
Examples
Incorrect: focusable descendant inside role="button"
<divrole="button">
<spantabindex="0">Click me</span>
</div>
The <span> with tabindex="0" creates a focusable element inside the role="button" container, which violates the ARIA authoring rules.
Incorrect: anchor element inside role="button"
<divrole="button"tabindex="0">
<ahref="/action"tabindex="0">Perform action</a>
</div>
Even though the container itself is focusable, the nested <a> with tabindex is also focusable, creating two tab stops for what should be a single control.
Correct: use a native <button> element
<button>Click me</button>
A native <button> handles focus, keyboard events, and accessibility semantics out of the box with no additional attributes.
Correct: move tabindex to the role="button" container
<divrole="button"tabindex="0">
<span>Click me</span>
</div>
The tabindex="0" is on the role="button" element itself, and the inner <span> is not independently focusable.
Correct: native button with styled inner content
<button>
<spanclass="icon">★</span>
<spanclass="label">Favorite</span>
</button>
You can still use inner elements for styling purposes inside a <button> — just don't add tabindex to them. The button manages focus as a single unit, and screen readers announce the combined text content.
The <meta charset> element tells the browser which character encoding to use when interpreting the bytes of the HTML document. The HTML specification explicitly states that there must be no more than one <meta> element with a charset attribute per document. This declaration should appear within the first 1024 bytes of the document, so placing it as the first child of <head> (right after the opening <head> tag) is the recommended practice.
Duplicate charset declarations typically happen when code is assembled from multiple templates, partials, or snippets — each contributing its own <meta charset>. It can also occur when a developer manually adds a charset declaration without realizing one is already present, or when migrating from an older <meta http-equiv="Content-Type"> approach and adding a new <meta charset> without removing the old equivalent.
Why this matters
- Standards compliance: The WHATWG HTML living standard mandates at most one
<meta charset>per document. Violating this produces a validation error. - Unpredictable behavior: When a browser encounters conflicting or duplicate charset declarations, the behavior is undefined. While most modern browsers will use the first one encountered, relying on this is fragile and could lead to garbled text or encoding issues in edge cases.
- Maintainability: Multiple charset declarations signal disorganized or duplicated template logic, making the codebase harder to maintain.
How to fix it
- Search your HTML document (including any templates, layouts, or partials that compose the final output) for all instances of
<meta charset>or<meta charset="...">. - Keep exactly one
<meta charset="utf-8">declaration, placed as the first element inside<head>. - Remove all other
<meta charset>elements. - If you also have a legacy
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">, remove it — the shorter<meta charset="utf-8">form is the modern replacement, and having both counts as duplicate charset declarations.
Examples
❌ Incorrect: multiple charset declarations
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
The second <meta charset="utf-8"> triggers the validation error, even though both specify the same encoding.
❌ Incorrect: mixing old and new charset syntax
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<metahttp-equiv="Content-Type"content="text/html; charset=utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
Both elements declare a character encoding, so the validator treats this as a duplicate.
✅ Correct: single charset declaration
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
A single <meta charset="utf-8"> appears first in <head>, before any other elements or content. This is the correct and recommended approach. UTF-8 is the strongly recommended encoding for all new HTML documents.
The HTML parser expects a slash inside a tag to signal the end of a self-closing (void) element, such as <br /> or <img />. When the parser encounters a / that isn't immediately followed by >, it can't interpret the tag correctly and raises this error. This can happen in several scenarios:
- A typo in an attribute name or value, such as accidentally typing a
/instead of another character. - An unquoted attribute value that contains a slash, like a URL path.
- A misplaced slash somewhere in the middle of a tag.
While browsers are generally forgiving and may still render the page, relying on error recovery leads to unpredictable behavior across different browsers. Fixing these issues ensures your markup is unambiguous, standards-compliant, and easier to maintain.
Examples
Stray slash in a tag (typo)
A common typo where a slash appears between attributes:
<!-- ❌ Slash not immediately followed by ">" -->
<inputtype="text" / name="username">
Remove the stray slash:
<!-- ✅ Correct -->
<inputtype="text"name="username">
Unquoted attribute value containing a slash
If an attribute value contains a / and isn't wrapped in quotes, the parser sees the slash as part of the tag syntax rather than the value:
<!-- ❌ Unquoted value with slash confuses the parser -->
<imgsrc=images/photo.jpgalt="Photo">
Always quote attribute values, especially those containing slashes:
<!-- ✅ Correct: quoted attribute value -->
<imgsrc="images/photo.jpg"alt="Photo">
Slash in the wrong position for self-closing tags
Placing the slash before the final attribute instead of at the end of the tag:
<!-- ❌ Slash is not immediately before ">" -->
<imgsrc="logo.png" / alt="Logo">
Move the self-closing slash to the very end, or simply remove it (self-closing syntax is optional in HTML5 for void elements):
<!-- ✅ Correct: slash at the end -->
<imgsrc="logo.png"alt="Logo"/>
<!-- ✅ Also correct: no self-closing slash needed in HTML -->
<imgsrc="logo.png"alt="Logo">
Accidental double slash
Sometimes a self-closing tag ends up with an extra slash:
<!-- ❌ Double slash before ">" -->
<br //>
Use a single slash or omit it entirely:
<!-- ✅ Correct -->
<br/>
<!-- ✅ Also correct -->
<br>
In CSS, a length value is composed of two parts: a number and a unit. Writing just px provides the unit but omits the number, which makes the declaration invalid. The CSS parser cannot interpret px alone as a meaningful measurement, so the property is ignored entirely. This means your intended layout won't be applied, potentially causing elements to render at unexpected sizes across different browsers.
This error commonly arises from typos, copy-paste mistakes, or templating issues where a variable that should output a number resolves to an empty string, leaving behind only the unit suffix. It can also happen when a numeric value is accidentally deleted during editing.
Beyond layout breakdowns, invalid CSS can cause inconsistent rendering across browsers. Some browsers may silently discard the invalid declaration, while others might apply unexpected fallback behavior. Keeping your CSS valid ensures predictable, cross-browser results and makes your stylesheets easier to maintain and debug.
How to Fix It
- Add the missing numeric value — Pair every unit with a number, e.g.,
300px,1.5em,50%. - Use
0without a unit for zero values — The value0is valid on its own and doesn't require a unit. - Use a valid keyword — Properties like
widthaccept keywords such asauto,min-content,max-content, andfit-content. - Check dynamic values — If a preprocessor or template engine generates the value, verify it outputs a complete length (e.g.,
${value}pxwherevalueis not empty).
Examples
Incorrect: unit without a number
<style>
.box{
width: px;
}
</style>
<divclass="box">Content</div>
The declaration width: px is invalid because px alone is not a recognized CSS value. The browser will discard this rule.
Incorrect: number without a unit
<style>
.box{
width:300;
}
</style>
<divclass="box">Content</div>
A unitless number (other than 0) is also invalid for the width property. Browsers will ignore this declaration as well.
Correct: number paired with a unit
<style>
.box{
width:300px;
}
</style>
<divclass="box">Content</div>
Correct: using different valid length units
<style>
.box-a{
width:50%;
}
.box-b{
width:20em;
}
.box-c{
width:15rem;
}
.box-d{
width:80vw;
}
</style>
Correct: zero value and keywords
<style>
.collapsed{
width:0;
}
.flexible{
width: auto;
}
.intrinsic{
width: fit-content;
}
</style>
The value 0 is the only number that doesn't require a unit in CSS. Keywords like auto, min-content, max-content, and fit-content are also valid for width and don't use numeric lengths at all.
Common CSS Length Units
| Unit | Description |
|---|---|
px | Pixels (absolute unit) |
em | Relative to the element's font size |
rem | Relative to the root element's font size |
% | Percentage of the containing block's dimension |
vw | 1% of the viewport width |
vh | 1% of the viewport height |
ch | Width of the "0" character in the element's font |
Always double-check that your CSS length values include both a number and a unit. If you're generating styles dynamically, add safeguards to ensure the numeric portion is never empty before the unit is appended.
The <label> element represents a caption for a form control. When you use the for attribute, its value must match the id of the form control it labels. According to the HTML specification, an id attribute value must not contain any whitespace characters — this includes spaces, tabs, line feeds, carriage returns, and form feeds. Since for must reference a valid ID, the same restriction applies to its value.
This error typically occurs when a developer uses a space-separated name (like "user name" or "first name") instead of a single continuous token. Browsers may fail to establish the programmatic association between the label and its form control when the for value contains whitespace. This directly impacts accessibility: screen readers rely on this association to announce the label text when a user focuses on the input. It also breaks the click-to-focus behavior where clicking a label moves focus to its associated control.
To fix this issue, replace any whitespace in the for attribute value with a valid character such as a hyphen (-), underscore (_), or camelCase. Make sure the id on the corresponding form control matches exactly.
Examples
Incorrect — whitespace in the for attribute
<form>
<labelfor="user name">Name</label>
<inputtype="text"id="user name">
</form>
The value "user name" contains a space, which makes it an invalid ID reference. The validator will report: Bad value "user name" for attribute "for" on element "label": An ID must not contain whitespace.
Correct — using an underscore instead of a space
<form>
<labelfor="user_name">Name</label>
<inputtype="text"id="user_name">
</form>
Correct — using a hyphen instead of a space
<form>
<labelfor="user-name">Name</label>
<inputtype="text"id="user-name">
</form>
Correct — using camelCase
<form>
<labelfor="userName">Name</label>
<inputtype="text"id="userName">
</form>
Incorrect — leading or trailing whitespace
Whitespace doesn't have to be in the middle of the value to trigger this error. Leading or trailing spaces also make the ID invalid:
<form>
<labelfor=" email">Email</label>
<inputtype="text"id=" email">
</form>
This can be easy to miss when values are generated dynamically or copied from another source. Trim the value to fix it:
<form>
<labelfor="email">Email</label>
<inputtype="text"id="email">
</form>
Alternative — wrapping the input inside the label
If you wrap the form control inside the <label> element, you don't need the for attribute at all. The association is implicit:
<form>
<label>
Name
<inputtype="text">
</label>
</form>
This approach avoids potential ID mismatches entirely, though explicit for/id pairing is often preferred for flexibility in layout and styling.
The gap property is a shorthand for row-gap and column-gap, used in CSS Grid, Flexbox, and multi-column layouts to define spacing between items or tracks. According to the CSS Box Alignment specification, the accepted values for gap are length values (px, em, rem, %, vh, etc.), the normal keyword, or the calc() function. The keyword auto — while valid for many other CSS properties like margin, width, and grid-template-columns — is simply not part of the gap property's value grammar.
This confusion often arises because developers are accustomed to using auto for spacing in other contexts. For instance, margin: auto is a common centering technique, and auto is widely used in grid track sizing. However, the gap property serves a different purpose: it defines a fixed or computed gutter size between items, and auto has no defined meaning in that context.
Why this matters
- Standards compliance: Using an invalid value means the browser will ignore the entire
gapdeclaration, falling back to the default value ofnormal(which is typically0pxin Grid and Flexbox). This can lead to unexpected layout results where items have no spacing at all. - Cross-browser consistency: While some browsers may be lenient with invalid CSS values, others will strictly discard them. This creates inconsistent layouts across different browsers and versions.
- Maintainability: Invalid CSS values can mask the developer's intent, making it harder for others (or your future self) to understand what spacing was desired.
How to fix it
Replace auto with a valid value for gap:
- A specific length:
gap: 16px;,gap: 1rem;,gap: 0.5em; - A percentage:
gap: 2%; - The
normalkeyword:gap: normal;(resolves to0pxin Flexbox and Grid) - A
calc()expression:gap: calc(1rem + 4px); - Two values for row and column separately:
gap: 16px 24px;
If you were using auto because you wanted the browser to determine the spacing dynamically, consider using a percentage value, a viewport-relative unit (vw, vh), or CSS clamp() for responsive gutters.
Examples
Incorrect: using auto as a gap value
<divstyle="display: grid;grid-template-columns:1fr1fr;gap: auto;">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
This triggers the validation error because auto is not a valid gap value. The browser will discard the declaration entirely.
Correct: using a fixed length
<divstyle="display: grid;grid-template-columns:1fr1fr;gap:16px;">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
Correct: using separate row and column gaps
<divstyle="display: grid;grid-template-columns:1fr1fr;gap:12px24px;">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
Correct: using a percentage for responsive spacing
<divstyle="display: flex;flex-wrap: wrap;gap:2%;">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
Correct: using clamp() for fluid responsive gaps
<divstyle="display: grid;grid-template-columns:1fr1fr;gap:clamp(8px,2vw,32px);">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
This approach gives you a gap that scales with the viewport width, bounded between 8px and 32px — a useful alternative if you were reaching for auto to get flexible spacing behavior.
An invalid value was assigned to the CSS visibility property inside your HTML document.
The visibility property controls whether an element is visually displayed without affecting the document layout. Unlike display: none, a hidden element still occupies space on the page.
The accepted values for visibility are:
visible— the element is shown (default).hidden— the element is invisible but still takes up space.collapse— used primarily with table rows and columns to remove them without affecting the table layout. On non-table elements, it behaves likehidden.
This error typically occurs when you use a value meant for a different property, such as none (which belongs to display), or a misspelled value like hiden or visble.
Invalid Example
<pstyle="visibility: none;">This text is hidden.</p>
The value none is not valid for visibility. You likely meant hidden or intended to use the display property instead.
Fixed Example
Using the correct visibility value:
<pstyle="visibility: hidden;">This text is hidden but still takes up space.</p>
Or, if you want the element to be fully removed from the layout, use display instead:
<pstyle="display: none;">This text is completely removed from the layout.</p>
The name attribute on <a> elements was historically used to create named anchors — fragment targets that could be linked to with href="#anchorName". In modern HTML (the WHATWG living standard), the name attribute on <a> is considered obsolete for this purpose. The id attribute is now the standard way to create fragment targets, and it can be placed on any element, not just <a> tags.
Regardless of whether you use name or id, the value must be a non-empty string. The W3C validator enforces this rule because an empty identifier serves no functional purpose — it cannot be referenced by a fragment link, it cannot be targeted by JavaScript, and it creates invalid markup. Browsers may silently ignore it, but it pollutes the DOM and signals a likely mistake in the code.
Empty name attributes often appear in content migrated from older CMS platforms or WYSIWYG editors that inserted placeholder anchors like <a name=""></a>. They can also result from templating systems where a variable intended to populate the attribute resolved to an empty string.
Why this matters
- Standards compliance: Both the WHATWG HTML living standard and the W3C HTML specification require that identifier-like attributes (
id,name) must not be empty strings. - Accessibility: Screen readers and assistive technologies may attempt to process named anchors. Empty identifiers create noise without providing any navigational value.
- Functionality: An empty
nameoridcannot be used as a fragment target, so the element is effectively useless as a link destination.
How to fix it
- Remove the element entirely if the empty anchor serves no purpose — this is the most common fix.
- Replace
namewithidand provide a meaningful, non-empty value if you need a fragment target. - Move the
idto a nearby semantic element instead of using a standalone empty<a>tag. For example, place theiddirectly on a heading, section, or paragraph. - Ensure uniqueness — every
idvalue in a document must be unique.
Examples
❌ Empty name attribute triggers the error
<aname=""></a>
<h2>Introduction</h2>
<p>Welcome to the guide.</p>
❌ Empty name generated by a template
<aname=""></a>
<p>This anchor was meant to be a target but the value is missing.</p>
<ahref="#">Jump to section</a>
✅ Remove the empty anchor if it's unnecessary
<h2>Introduction</h2>
<p>Welcome to the guide.</p>
✅ Use id on the target element directly
<h2id="introduction">Introduction</h2>
<p>Welcome to the guide.</p>
<!-- Link to the section from elsewhere -->
<ahref="#introduction">Go to Introduction</a>
✅ Use id on a standalone anchor if needed
If you need a precise anchor point that doesn't correspond to an existing element, use an <a> tag with a valid, non-empty id:
<aid="section-start"></a>
<p>This paragraph follows the anchor point.</p>
<ahref="#section-start">Jump to section start</a>
✅ Migrate legacy name to id
If your existing code uses the obsolete name attribute with a valid value, update it to use id instead:
<!-- Before (obsolete but was valid in HTML4) -->
<aname="contact"></a>
<!-- After (modern HTML) -->
<aid="contact"></a>
<!-- Even better: put the id on a semantic element -->
<h2id="contact">Contact Us</h2>
In CSS, property values such as lengths, percentages, and keywords are written without quotation marks. Quotes in CSS are reserved for specific contexts like content property strings, font family names with spaces, and url() paths. When you wrap a margin value (or any similar CSS property value) in double or single quotes, the CSS parser interprets it as a string literal rather than a set of length or keyword values. Since "0 0 1em 0" is a string and not a valid margin value, the declaration is ignored by browsers and flagged by the W3C validator.
This is a problem for several reasons. First, the style will silently fail — browsers discard CSS declarations they can't parse, so your intended margins won't be applied, potentially breaking your layout. Second, it indicates a misunderstanding of CSS syntax that could lead to similar errors in other properties. This mistake commonly occurs when developers confuse HTML attribute quoting rules with CSS value syntax, especially when writing inline style attributes where the attribute value itself is already quoted.
The margin property accepts one to four values, each of which can be a length (e.g., 10px, 1em), a percentage, auto, or a global keyword like inherit. None of these require quotes. The fix is straightforward: remove the quotation marks around the CSS value.
Examples
❌ Incorrect: margin value wrapped in quotes
In a <style> block:
<style>
.card{
margin:"0 0 1em 0";
}
</style>
In an inline style:
<pstyle="margin:'10px auto'">Hello</p>
Both of these produce the validator error because the CSS parser sees a quoted string instead of valid margin values.
✅ Correct: margin value without quotes
In a <style> block:
<style>
.card{
margin:001em0;
}
</style>
In an inline style:
<pstyle="margin: 10px auto">Hello</p>
Valid margin value formats
For reference, here are the accepted patterns for the margin property — none of which use quotes:
/* All four sides */
margin:1em;
/* Vertical | Horizontal */
margin:5% auto;
/* Top | Horizontal | Bottom */
margin:1em auto 2em;
/* Top | Right | Bottom | Left */
margin:2px1em0 auto;
/* Global keywords */
margin: inherit;
margin: initial;
margin: unset;
Watch out for inline style quoting confusion
A common source of this mistake is confusion about the quotes used for the HTML style attribute versus the CSS values inside it. The outer quotes delimit the attribute value for HTML — the CSS inside should not have its own quotes around property values:
<!-- ❌ Wrong: extra quotes around the CSS value -->
<divstyle="margin:'1em'"></div>
<!-- ✅ Correct: only the HTML attribute is quoted -->
<divstyle="margin: 1em"></div>
This same rule applies to other CSS properties like padding, border, font-size, color, and so on. If you see a similar validator error for any CSS property, check whether you've accidentally quoted the value.
The for attribute on a <label> element tells the browser which form control the label describes. When a user clicks or taps the label, the browser transfers focus to the associated control. For this to work, the value of for must exactly match the id of a form element such as an <input>, <textarea>, <select>, or <button>.
An empty string ("") is not a valid ID according to the HTML specification. The WHATWG HTML standard requires that an id attribute value must contain at least one character and must not contain ASCII whitespace. Since no element can have an empty-string id, a <label> with for="" can never successfully reference anything, making it both invalid markup and a broken association.
Why this matters
Accessibility: Screen readers rely on the for/id pairing to announce what a form control is for. A label with an empty for attribute creates no programmatic association, meaning assistive technology users may not know what a field is asking for. This directly impacts WCAG compliance.
Usability: A properly associated label expands the clickable area of its form control. For example, clicking a label associated with a checkbox will toggle the checkbox. An empty for attribute breaks this behavior.
Standards compliance: The W3C validator flags this because it violates the HTML specification. Keeping markup valid helps ensure consistent behavior across browsers and future-proofs your code.
How to fix
You have three options:
- Set
forto a validid: Give the associated form control a uniqueidand reference it in the label'sforattribute. - Remove
forand use implicit association: Wrap the form control inside the<label>element. This creates an implicit association without needingfororidat all. - Remove the
forattribute: If the label is purely decorative or not meant to be associated with a control, simply remove the emptyforattribute.
Examples
❌ Empty for attribute (triggers the error)
<labelfor="">Username:</label>
<inputtype="text"name="username">
The label has no meaningful association with the input because for="" is not a valid reference.
✅ Fix: Use a valid for/id pair
<labelfor="username">Username:</label>
<inputtype="text"id="username"name="username">
The for="username" now matches id="username" on the input, creating an explicit association.
✅ Fix: Use implicit association by nesting
<label>
Username:
<inputtype="text"name="username">
</label>
Wrapping the input inside the <label> creates an implicit association. No for or id attributes are needed.
❌ Multiple labels with empty for attributes
<form>
<labelfor="">Email:</label>
<inputtype="email"name="email">
<labelfor="">Subscribe to newsletter</label>
<inputtype="checkbox"name="subscribe">
</form>
✅ Fixed with proper associations
<form>
<labelfor="email">Email:</label>
<inputtype="email"id="email"name="email">
<labelfor="subscribe">Subscribe to newsletter</label>
<inputtype="checkbox"id="subscribe"name="subscribe">
</form>
Each id must be unique within the document, and each for attribute must reference exactly one id. If your labels are generated by a framework or CMS with empty for values, check the template or component configuration to ensure proper id values are being output.
When the W3C HTML Validator reports that an attribute is "not serializable as XML 1.0," it means the attribute name contains characters that fall outside the allowed range defined by the XML 1.0 specification. HTML5 documents can be serialized as either HTML or XML (XHTML), and the validator checks that your markup is compatible with both serialization formats. Attribute names in XML 1.0 must start with a letter or underscore and can only contain letters, digits, hyphens, underscores, periods, and certain Unicode characters — they cannot include characters like {, }, @, $, or other symbols commonly found in templating languages.
This issue most frequently appears when a server-side or client-side templating engine (such as Mustache, Handlebars, Angular, Jinja2, or Blade) fails to fully process its expressions before the HTML reaches the browser. Instead of the template placeholder being replaced with a proper value, the raw template syntax ends up in the HTML as a malformed attribute. For example, {{class}} might appear as an attribute name rather than being resolved to its intended value.
Why this matters
- Standards compliance: HTML that isn't serializable as XML 1.0 cannot be reliably converted to XHTML, which limits interoperability.
- Browser inconsistency: Browsers may handle invalid attribute names unpredictably, potentially ignoring the attribute entirely or misinterpreting surrounding markup.
- Accessibility: Malformed attributes can break ARIA attributes or other accessibility-related markup, making content inaccessible to assistive technologies.
- Tooling and parsing: XML-based tools, RSS feeds, and content syndication systems that consume your HTML will choke on attributes that violate XML naming rules.
How to fix it
- Check for unresolved template expressions. Inspect the rendered HTML (not your source templates) for leftover placeholders like
{{...}},<%= ... %>,@{...}, or similar patterns. - Ensure proper server-side rendering. Make sure your templating engine is correctly processing all expressions before the HTML is sent to the client.
- Remove invalid characters from attribute names. If you're using custom attributes, stick to valid
data-*attributes with names consisting only of lowercase letters, digits, and hyphens (after thedata-prefix). - Check for typos. A missing
=sign, quote, or space can cause a value to be interpreted as an attribute name.
Examples
Unresolved template placeholder in an attribute
This occurs when a template expression isn't processed and appears literally in the output:
<!-- ❌ Bad: template syntax rendered as attribute name -->
<div{{classBinding}}id="main">
<p>Hello, world!</p>
</div>
The fix is to ensure the template engine resolves the expression. The rendered output should look like this:
<!-- ✅ Good: attribute properly resolved -->
<divclass="container"id="main">
<p>Hello, world!</p>
</div>
Typo causing a value to be parsed as an attribute name
A missing equals sign or quotation mark can cause part of a value to become an attribute name with invalid characters:
<!-- ❌ Bad: missing = sign causes "bold}" to be treated as an attribute -->
<pstyle"font-weight:bold}" class="intro">Welcome</p>
<!-- ✅ Good: proper attribute syntax -->
<pstyle="font-weight:bold"class="intro">Welcome</p>
Special characters in custom attribute names
Using invalid characters directly in attribute names will trigger this error:
<!-- ❌ Bad: @ and $ are not valid in attribute names -->
<input@change="update"$value="test">
If you need custom attributes, use the standard data-* pattern:
<!-- ✅ Good: valid data attributes -->
<inputdata-change="update"data-value="test">
Angular-style bindings in static HTML
Frameworks like Angular use special attribute syntax (e.g., [property] or (event)) that is only valid within the framework's context and will fail validation if rendered directly:
<!-- ❌ Bad: framework-specific syntax in raw HTML -->
<img[src]="imageUrl"(load)="onLoad()">
If you're generating static HTML, use standard attributes instead:
<!-- ✅ Good: standard HTML attribute -->
<imgsrc="photo.jpg"alt="A photo">
The transform CSS property lets you rotate, scale, skew, or translate an element by modifying its coordinate space. The validator checks inline and embedded CSS for correctness, and it will flag any value it doesn't recognize as a valid transform value. Common mistakes include:
- Missing units on angles or lengths (e.g.,
rotate(45)instead ofrotate(45deg)) - Typos in function names (e.g.,
rotatee(10deg)ortranlate(10px)) - Wrong value types (e.g., using a color or a plain number where a function is expected)
- Missing commas or parentheses in function arguments
- Using non-existent functions (e.g.,
flip(180deg)is not a valid transform function) - Incorrect number of arguments (e.g.,
matrix()requires exactly 6 values)
This matters for standards compliance and predictable rendering. While browsers may silently ignore invalid transform values, the element simply won't be transformed — which can lead to subtle layout bugs that are hard to track down. Catching these errors at validation time helps you fix them before they reach users.
Examples
Invalid: missing angle unit
The rotate() function requires a value with an angle unit like deg, rad, turn, or grad.
<divstyle="transform:rotate(45);">Rotated text</div>
Fixed: adding the angle unit
<divstyle="transform:rotate(45deg);">Rotated text</div>
Invalid: typo in function name
<divstyle="transform:tranlateX(10px);">Shifted text</div>
Fixed: correcting the function name
<divstyle="transform:translateX(10px);">Shifted text</div>
Invalid: using a non-transform value
A plain number or unrelated keyword is not a valid transform value.
<divstyle="transform:200px;">Content</div>
Fixed: using a proper transform function
<divstyle="transform:translateX(200px);">Content</div>
Invalid: wrong number of arguments for matrix()
The matrix() function requires exactly six comma-separated numbers.
<divstyle="transform:matrix(1,2,3);">Content</div>
Fixed: providing all six arguments
<divstyle="transform:matrix(1,0,0,1,0,0);">Content</div>
Valid transform values reference
Here is a summary of all valid transform functions and the keyword/global values:
<style>
/* Keyword value */
.no-transform{transform: none;}
/* Translate functions */
.move-a{transform:translate(12px,50%);}
.move-b{transform:translateX(2em);}
.move-c{transform:translateY(3in);}
.move-d{transform:translateZ(2px);}
.move-e{transform:translate3d(12px,50%,3em);}
/* Rotate functions */
.spin-a{transform:rotate(0.5turn);}
.spin-b{transform:rotateX(10deg);}
.spin-c{transform:rotateY(10deg);}
.spin-d{transform:rotateZ(10deg);}
.spin-e{transform:rotate3d(1,2,3,10deg);}
/* Scale functions */
.grow-a{transform:scale(2,0.5);}
.grow-b{transform:scaleX(2);}
.grow-c{transform:scaleY(0.5);}
.grow-d{transform:scaleZ(0.3);}
.grow-e{transform:scale3d(2.5,1.2,0.3);}
/* Skew functions */
.lean-a{transform:skew(30deg,20deg);}
.lean-b{transform:skewX(30deg);}
.lean-c{transform:skewY(1.07rad);}
/* Other functions */
.depth{transform:perspective(500px);}
.matrix-2d{transform:matrix(1,0,0,1,0,0);}
.matrix-3d{transform:matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);}
/* Multiple functions chained together */
.combo{transform:translateX(10px)rotate(10deg)translateY(5px);}
</style>
When troubleshooting this error, look at the specific value the validator reports as invalid. Compare it against the valid functions listed above, double-check spelling, ensure all arguments have correct units, and verify that parentheses and commas are properly placed.
The validator reports “Bad value “” for attribute id on element X: An ID must not be the empty string” when any element includes an empty id attribute. Per the HTML standard, id is a global attribute used as a unique document-wide identifier. An empty identifier is not a valid value and is ignored by some features, leading to hard-to-debug issues.
This matters for accessibility and interoperability. Features that depend on IDs—fragment navigation (#target), <label for>, ARIA attributes like aria-labelledby/aria-controls, and DOM APIs such as document.getElementById()—require a non-empty, unique value. Empty IDs break these links, can degrade assistive technology output, and violate conformance, which may hide bugs across browsers.
How to fix:
- If the element doesn’t need an identifier, remove the
idattribute entirely. - If it needs one, provide a non-empty, unique value, e.g.,
id="main-content". - Ensure uniqueness across the page; each
idmust occur only once. - Use simple, predictable tokens: avoid spaces, prefer lowercase letters, digits, hyphens, and underscores (e.g.,
feature-1). While the spec allows a broad range of characters, sticking to URL- and selector-friendly characters avoids pitfalls.
Examples
Example that triggers the validator error (empty id)
<divid=""></div>
Correct: remove an unnecessary empty id
<div></div>
Correct: provide a meaningful, unique id
<sectionid="features"></section>
Problematic label association with empty id (invalid)
<labelfor="">Email</label>
<inputtype="email"id="">
Correct label–control association
<labelfor="email">Email</label>
<inputtype="email"id="email">
Correct ARIA relationship
<h2id="pricing-heading">Pricing</h2>
<sectionaria-labelledby="pricing-heading">
<p>Choose a plan.</p>
</section>
Correct fragment navigation target
<nav>
<ahref="#contact">Contact</a>
</nav>
<sectionid="contact">
<h2>Contact us</h2>
</section>
Minimal full document (validated) demonstrating proper ids
<!doctype html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>Valid IDs Example</title>
</head>
<body>
<mainid="main-content">
<h1id="page-title">Welcome</h1>
<p>Jump to the <ahref="#details">details</a>.</p>
<sectionid="details">
<h2>Details</h2>
</section>
<form>
<labelfor="email">Email</label>
<inputid="email"type="email">
</form>
</main>
</body>
</html>
The padding shorthand property sets the padding area on all four sides of an element. It accepts one to four values, each of which must be a <length> (e.g., 10px, 1em), a <percentage>, or 0. Unlike some other CSS properties such as border, outline, or max-width, the padding property has no none keyword in its value syntax.
This is a common mistake because several CSS properties do accept none — for example, border: none, text-decoration: none, and display: none. It's natural to assume padding: none would work the same way, but the CSS specification simply doesn't define it for padding. When a browser encounters an invalid value, it ignores the declaration entirely, which means your intended styling won't be applied and the element may retain its default or inherited padding. This can lead to unexpected layout issues that are difficult to debug.
The same rule applies to the margin property — margin: none is also invalid. Use margin: 0 instead.
How to Fix It
Replace none with 0. You don't need to include a unit when the value is zero, so padding: 0 is perfectly valid and is the idiomatic way to express "no padding." You can also use 0 for individual padding properties like padding-top, padding-right, padding-bottom, and padding-left.
If you only want to remove padding on specific sides, target those sides individually rather than using the shorthand.
Examples
❌ Incorrect: Using none with padding
.card{
padding: none;
}
The validator will report: CSS: "padding": "none" is not a "padding" value. The browser will ignore this declaration.
✅ Correct: Using 0 to remove padding
.card{
padding:0;
}
✅ Correct: Removing padding on specific sides
.card{
padding-top:0;
padding-bottom:0;
}
❌ Incorrect: Using none in inline styles
<divstyle="padding: none;">Content</div>
✅ Correct: Using 0 in inline styles
<divstyle="padding:0;">Content</div>
✅ Correct: Using valid padding values
/* Single value — applies to all four sides */
.card{
padding:16px;
}
/* Two values — vertical | horizontal */
.card{
padding:10px20px;
}
/* Four values — top | right | bottom | left */
.card{
padding:10px20px15px5px;
}
/* Zero on top/bottom, 1em on left/right */
.card{
padding:01em;
}
CSS length values must always pair a number with a unit — writing just px, em, %, or any other unit without a preceding number is meaningless to the browser and will be ignored. This typically happens due to a typo, a copy-paste error, or a build tool / template that outputs a unit without its corresponding numeric value (e.g., a variable that resolved to an empty string concatenated with px).
When the W3C validator encounters margin: px in an inline style attribute, it flags the error because px on its own does not match any valid CSS value for the margin property. Valid values include lengths like 10px or 2em, percentages like 5%, the keyword auto, or the number 0 (which doesn't need a unit). Browsers will discard the invalid declaration, meaning your intended spacing won't be applied — potentially breaking your layout in subtle ways that are hard to debug.
This issue also applies to other CSS properties that accept length values, such as padding, width, height, top, left, border-width, font-size, and many more. The fix is always the same: ensure every unit has an accompanying number.
How to Fix It
- Add the missing number before the unit: change
pxto something like10px,1.5em, or20%. - Use
0without a unit if you want zero margin — writingmargin: 0is valid and preferred overmargin: 0px. - Use a keyword if appropriate, such as
margin: autofor centering. - Remove the declaration if the margin value was unintentional or unnecessary.
If the value comes from a preprocessor, template engine, or JavaScript, check that the variable being interpolated is not empty or undefined before it gets concatenated with the unit string.
Examples
Incorrect: Unit Without a Number
<divstyle="margin: px;">Content</div>
The value px has no number, so this is invalid CSS.
Correct: Number Paired With Unit
<divstyle="margin:10px;">Content</div>
Correct: Zero Margin (No Unit Needed)
<divstyle="margin:0;">Content</div>
Correct: Using a Keyword
<divstyle="margin: auto;">Content</div>
Incorrect in an External Stylesheet
This same error can appear in a <style> block or linked stylesheet:
<style>
.card{
margin: px;
}
</style>
Correct in an External Stylesheet
<style>
.card{
margin:16px;
}
</style>
Watch Out for Template Variables
A common cause in templating systems is an empty variable:
<!-- If spacing is empty, this produces "margin: px;" -->
<divstyle="margin:{{ spacing }}px;">Content</div>
To prevent this, ensure the variable contains the full value including the number, or add a fallback:
<divstyle="margin:16px;">Content</div>
The CSS cursor property controls the appearance of the mouse pointer when it hovers over an element. The value hand was introduced by early versions of Internet Explorer (IE 5.5 and earlier) as a proprietary extension to show a pointing-hand cursor over clickable elements. However, this value was never part of any CSS specification, and no other browser adopted it. The W3C-standard equivalent is pointer, which has been supported by all browsers — including Internet Explorer 6 and later — for over two decades.
When the W3C validator encounters cursor: hand, it flags it as an invalid value because hand does not exist in the CSS specification's list of accepted cursor values. While some legacy browsers may still interpret it, modern browsers will simply ignore the invalid declaration, meaning your clickable elements won't display the expected hand cursor for many users.
Beyond validation, using non-standard CSS values can cause inconsistent behavior across browsers and platforms. The pointer value is universally supported and is the correct way to signal that an element is interactive, such as a link, button, or any custom clickable region.
To fix this issue, replace every instance of cursor: hand with cursor: pointer in your stylesheets. If you need to support extremely old versions of Internet Explorer (IE 5.5 or earlier), you can declare both values — the browser will use whichever it recognizes — though this is almost never necessary today.
Examples
Invalid CSS
The value hand is not recognized by the CSS specification and will trigger a validation error:
.clickable{
cursor: hand;
}
Valid CSS
Use the standard pointer value instead:
.clickable{
cursor: pointer;
}
Using it in context with HTML
<style>
.card{
padding:16px;
border:1px solid #ccc;
cursor: pointer;
}
</style>
<divclass="card">
Click me to view details
</div>
Legacy fallback (rarely needed)
If for some reason you must support IE 5.5 or earlier alongside modern browsers, you can provide both declarations. The browser will apply the last value it understands:
.clickable{
cursor: hand;
cursor: pointer;
}
Note that this fallback pattern will still produce a validation warning for the hand value. In practice, there is virtually no reason to support browsers this old, so using cursor: pointer alone is the recommended approach.
Common cursor values
For reference, here are some of the most frequently used valid cursor values defined in the CSS specification:
auto— the browser determines the cursor based on context (default behavior)default— the platform's default cursor, typically an arrowpointer— a pointing hand, indicating a link or clickable elementtext— an I-beam, indicating selectable textmove— indicates something can be movednot-allowed— indicates an action is not permittedgrab/grabbing— indicates a draggable elementcrosshair— a precise selection cursorwait— indicates the program is busyhelp— indicates help is available
The full list of accepted values is defined in the CSS Basic User Interface Module specification.
An invalid value was assigned to the CSS right property, meaning the validator does not recognize the value you provided.
The CSS right property specifies the horizontal offset of a positioned element from the right edge of its containing block. It only accepts specific value types: a length (e.g., 10px, 2em), a percentage (e.g., 50%), auto, inherit, initial, unset, or revert. Any other value — such as a typo, a missing unit, or an unsupported keyword — will trigger this validation error.
A common mistake is forgetting the unit after a number. In CSS, 0 is the only length value that can be written without a unit. Writing something like right: 10 instead of right: 10px is invalid. Another common cause is using an unrecognized keyword or passing a value meant for a different property.
Invalid Example
<divstyle="position: absolute;right:10;">
This box has an invalid right value.
</div>
The value 10 is missing a unit, so the validator rejects it.
Fixed Example
<divstyle="position: absolute;right:10px;">
This box is correctly positioned.
</div>
Adding a valid unit like px, em, rem, or % resolves the issue. If you intended no offset, use right: 0 or right: auto.
The padding property accepts one or more length values, percentages, or the keyword 0. A valid length value always consists of a number immediately followed by a unit identifier, such as 10px, 1.5em, or 2rem. Writing just px without a preceding number is meaningless to the CSS parser — it's like saying "pixels" without specifying how many. The browser will discard the invalid declaration entirely, which means the element will fall back to its default or inherited padding, potentially breaking your layout in unexpected ways.
This error commonly occurs due to:
- Typos or accidental deletion — the numeric part of the value was inadvertently removed during editing.
- Templating or build tool issues — a dynamic value (e.g., from a variable or CMS field) resolved to an empty string, leaving only the
pxsuffix behind. - Copy-paste mistakes — copying a snippet and forgetting to update the placeholder value.
Because the W3C validator flags this in inline style attributes, it means invalid CSS is embedded directly in your HTML. Fixing it improves standards compliance and ensures consistent rendering across browsers.
How to Fix
- Add a numeric value before the unit: change
pxto something like10px,1em, or5%. - Use
0without a unit if you want zero padding — writingpadding: 0;is valid and preferred overpadding: 0px;. - Check dynamic values — if the number comes from a variable or template expression, make sure it outputs a valid number and isn't empty.
Examples
Incorrect: Unit Without a Number
<divstyle="padding: px;">Content</div>
The validator reports that px is not a valid padding value because no number precedes the unit.
Correct: Numeric Value With Unit
<divstyle="padding:10px;">Content</div>
Correct: Zero Padding (No Unit Needed)
<divstyle="padding:0;">Content</div>
When the value is 0, no unit is required since zero pixels, zero ems, and zero percent are all identical.
Correct: Multiple Padding Values
<divstyle="padding:8px16px;">Content</div>
This sets 8px of vertical padding and 16px of horizontal padding — both are valid length values.
Incorrect in External CSS
The same problem can appear in a stylesheet linked from your HTML:
.card{
padding: px;
}
Fixed in External CSS
.card{
padding:12px;
}
Watch for Template-Generated Values
If you use a templating system, double-check that the numeric portion actually renders. For example, a template like this could produce the error if spacing is empty:
<!-- If spacing is empty, this becomes "padding: px;" -->
<divstyle="padding:{{ spacing }}px;">Content</div>
Make sure the variable always resolves to a valid number, or provide a fallback value.
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