HTML Guides for CSS
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.
In CSS, most numeric values of 0 don't need a unit — for example, margin: 0 is perfectly valid because the specification allows unitless zero for <length> values. However, this exception does not apply to <time> values. Properties that accept <time> values, such as transition-delay, transition-duration, animation-delay, and animation-duration, always require a unit (s for seconds or ms for milliseconds), even when the value is zero.
The CSS specification explicitly states that <time> values must include a unit. The unitless 0 shorthand is only permitted for <length> and a few other value types. While some browsers may silently accept transition-delay: 0 and treat it as 0s, this behavior is non-standard and not guaranteed across all browsers or future implementations. Relying on it can lead to inconsistent rendering and will fail W3C CSS validation.
This issue commonly appears when transition-delay is set as part of the transition shorthand, or when developers assume that 0 is universally valid without a unit in CSS.
How to fix it
Add the s (seconds) or ms (milliseconds) unit to any <time> value that is currently a bare 0:
0→0sor0ms- Check both longhand properties (
transition-delay,transition-duration) and thetransitionshorthand.
Examples
Incorrect — unitless zero
<style>
.fade {
transition-delay: 0;
transition-duration: 0.3s;
transition-property: opacity;
}
</style>
<div class="fade">Hello</div>
The validator reports: CSS: "transition-delay": "0" is not a "transition-delay" value.
Correct — with time unit
<style>
.fade {
transition-delay: 0s;
transition-duration: 0.3s;
transition-property: opacity;
}
</style>
<div class="fade">Hello</div>
Incorrect — unitless zero in the transition shorthand
<style>
.btn {
transition: background-color 0.2s ease 0;
}
</style>
<button class="btn">Click me</button>
The fourth value in the transition shorthand is the delay, and 0 without a unit is invalid.
Correct — shorthand with time unit
<style>
.btn {
transition: background-color 0.2s ease 0s;
}
</style>
<button class="btn">Click me</button>
Multiple transitions
When specifying delays for multiple properties, ensure every <time> value has a unit:
<style>
.card {
transition-property: opacity, transform;
transition-duration: 0.3s, 0.5s;
transition-delay: 0s, 0.1s;
}
</style>
<div class="card">Content</div>
The same rule applies to transition-duration and the animation-delay and animation-duration properties — always include s or ms, even for zero values.
CSS distinguishes between pseudo-classes and pseudo-elements using different colon syntax. Pseudo-classes like :hover, :focus, and :active describe a temporary state of an element and use a single colon (:). Pseudo-elements like ::before, ::after, and ::first-line target a specific part of an element's rendering and use double colons (::). Writing ::hover conflates these two concepts — there is no pseudo-element called hover in any CSS specification.
This matters for several reasons. First, most browsers will silently ignore the invalid ::hover rule entirely, meaning your hover styles simply won't apply. Users will see no visual feedback when hovering over interactive elements like links and buttons, which hurts usability. Second, the lack of hover feedback can be an accessibility concern — sighted users rely on hover states to identify clickable elements. Third, invalid CSS can cause unpredictable behavior across different browsers and versions, making your site harder to maintain.
The confusion often arises because CSS2 originally allowed single colons for pseudo-elements (e.g., :before), and CSS3 introduced the double-colon syntax to clearly separate pseudo-elements from pseudo-classes. This means you might see both :before and ::before in the wild, which can make it tempting to assume that double colons work everywhere. The key rule to remember: states use one colon (:hover, :focus, :visited), and sub-element targets use two colons (::before, ::after, ::placeholder).
Examples
Incorrect: using double colons with hover
a::hover {
color: red;
}
button::hover {
background-color: blue;
}
Both rules above will trigger the validation error and will likely be ignored by browsers.
Correct: using a single colon with hover
a:hover {
color: red;
}
button:hover {
background-color: blue;
}
Correct usage of pseudo-classes vs. pseudo-elements
This example demonstrates how single-colon pseudo-classes and double-colon pseudo-elements are used together correctly:
a:hover {
color: red;
}
a:focus {
outline: 2px solid blue;
}
a::before {
content: "→ ";
}
Full HTML document with valid :hover usage
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hover Example</title>
<style>
a:hover {
color: red;
text-decoration: underline;
}
</style>
</head>
<body>
<a href="#">Hover over this link</a>
</body>
</html>
Quick Reference
| Type | Syntax | Examples |
|---|---|---|
| Pseudo-class (state) | Single colon : | :hover, :focus, :active, :visited, :first-child |
| Pseudo-element (sub-part) | Double colon :: | ::before, ::after, ::first-line, ::placeholder |
If you encounter this validation error, search your stylesheets for ::hover and replace every instance with :hover. The same fix applies if you accidentally use double colons with other pseudo-classes like ::focus or ::active.
The @font-feature-values at-rule lets you define human-readable names for OpenType font feature indexes, which you can then reference using properties like font-variant-alternates. For example, instead of remembering that swash index 1 maps to a "fancy" style, you can define a named value and use it semantically throughout your CSS. This is a legitimate and useful feature defined in the CSS Fonts Module Level 4 specification.
The validation error occurs because the W3C CSS validator does not always keep pace with newer CSS specifications. The @font-feature-values rule, along with its associated feature type blocks like @swash, @styleset, @character-variant, @ornaments, @annotation, and @stylistic, may simply not be in the validator's recognized grammar yet. This does not mean your CSS is broken or invalid — it means the validator has a gap in its coverage.
That said, there are practical reasons to consider alternatives. If you need to pass strict W3C validation (for example, as a project requirement or contractual obligation), or if you need to support older browsers that lack @font-feature-values support, the font-feature-settings property offers a more widely recognized way to activate OpenType features. The tradeoff is that font-feature-settings uses raw four-character OpenType feature tags instead of friendly names, making it less readable but more portable.
How to Fix
You have several options:
- Ignore the warning. If your target browsers support
@font-feature-values, the CSS is valid per the spec. The validator error is a false positive. - Move the at-rule to an external stylesheet. If you're validating HTML and the CSS is in a
<style>block, moving it to an external.cssfile may help you separate concerns and skip CSS validation during HTML checks. - Replace with
font-feature-settings. Use the lower-level property to activate OpenType features directly by their tag codes.
Examples
Code that triggers the validation error
@font-feature-values "MyFamily" {
@swash {
fancy: 1;
}
}
p {
font-family: "MyFamily", serif;
font-variant-alternates: swash(fancy);
}
The validator does not recognize @font-feature-values and flags it as an error, even though this is spec-compliant CSS.
Fixed: Using font-feature-settings instead
<!DOCTYPE html>
<html lang="en">
<head>
<title>Font Feature Example</title>
<style>
p {
font-family: "MyFamily", serif;
font-feature-settings: "swsh" 1;
}
</style>
</head>
<body>
<p>This text uses OpenType swash glyphs via font-feature-settings.</p>
</body>
</html>
The font-feature-settings property accepts OpenType feature tags directly. Common tags include "swsh" for swashes, "smcp" for small caps, "liga" for standard ligatures, and "onum" for oldstyle numerals. This approach avoids the unrecognized at-rule error entirely.
Fixed: Keeping @font-feature-values with a fallback
If you want to use the more readable @font-feature-values syntax while also providing fallback support, you can combine both approaches:
p {
font-family: "MyFamily", serif;
font-feature-settings: "swsh" 1;
font-variant-alternates: swash(fancy);
}
@font-feature-values "MyFamily" {
@swash {
fancy: 1;
}
}
Browsers that understand font-variant-alternates and @font-feature-values will use the named value. Others will fall back to font-feature-settings. The validation error will persist with this approach, but your CSS will be robust and spec-compliant regardless.
The @view-transition at-rule is valid CSS but is not yet recognized by the W3C CSS validator because it is a relatively new feature defined in the CSS View Transitions Module Level 2 specification.
The @view-transition at-rule opts a document into cross-document view transitions when navigating between two same-origin pages. It is placed in the CSS of the destination page (the page being navigated to) and accepts a navigation descriptor that controls when the transition activates.
@view-transition {
navigation: auto;
}
The navigation descriptor accepts these values:
none— no cross-document view transition occurs (default).auto— the transition activates for same-origin navigations where the navigation type is traverse, push, or replace, as long as the navigation does not include a cross-origin redirect.
Because the W3C CSS validator has not yet added support for this at-rule, the warning cannot be fixed by changing your code. The CSS itself is correct per the specification. Browser support is available in Chromium-based browsers (Chrome 126+, Edge 126+).
You can safely ignore this validator warning. If you want a completely clean validation output, move the @view-transition rule into a separate stylesheet so it does not interfere with validation of the rest of your CSS, or suppress the warning in your CI pipeline.
The vertical-align property controls the vertical positioning of inline-level elements (like <span>, <img>, and <a>) and table-cell elements (<td>, <th>) relative to their surrounding content or cell. Unlike some other CSS properties (such as float or border), vertical-align has no none keyword. Attempting to use none results in an invalid declaration that browsers will ignore, meaning the element will fall back to the default value of baseline.
This mistake often happens when a developer wants to "reset" or "remove" vertical alignment. Since there is no none value, the correct approach is to either set vertical-align: baseline (the initial value) or remove the vertical-align declaration altogether.
The valid keyword values for vertical-align are:
baseline— aligns the element's baseline with the parent's baseline (default)sub— aligns as a subscriptsuper— aligns as a superscripttext-top— aligns with the top of the parent's fonttext-bottom— aligns with the bottom of the parent's fontmiddle— aligns the middle of the element with the baseline plus half the x-height of the parenttop— aligns the top of the element with the top of the tallest element on the linebottom— aligns the bottom of the element with the bottom of the lowest element on the line
In addition to keywords, vertical-align also accepts length values (e.g., 5px, 0.5em) and percentage values (e.g., 50%), which offset the element relative to the baseline.
Using an invalid value like none causes a W3C validation error and means your intended styling is silently ignored by the browser. This can lead to unexpected layout results that are difficult to debug, especially in table layouts or inline formatting contexts where vertical alignment significantly affects appearance.
Examples
❌ Invalid: using none
<p>
Text with an <img src="icon.png" alt="icon" style="vertical-align: none;"> inline image.
</p>
The validator will report that none is not a valid vertical-align value. The browser ignores the declaration and defaults to baseline.
✅ Fixed: using a valid keyword
If you want the image vertically centered with the text, use middle:
<p>
Text with an <img src="icon.png" alt="icon" style="vertical-align: middle;"> inline image.
</p>
✅ Fixed: resetting to the default
If your intent was to "remove" any vertical alignment, use baseline (the initial value) or simply remove the property:
<p>
Text with an <img src="icon.png" alt="icon" style="vertical-align: baseline;"> inline image.
</p>
❌ Invalid: none in a stylesheet for table cells
<style>
td.reset {
vertical-align: none;
}
</style>
<table>
<tr>
<td class="reset">Cell content</td>
</tr>
</table>
✅ Fixed: valid value for table cells
For table cells, the default vertical-align value is middle in most browsers. To explicitly reset it or set a specific alignment:
<style>
td.top-aligned {
vertical-align: top;
}
</style>
<table>
<tr>
<td class="top-aligned">Cell content</td>
</tr>
</table>
✅ Fixed: using a length value
You can also use a specific length to offset the element from the baseline:
<p>
Text with a <span style="vertical-align: 4px;">slightly raised</span> word.
</p>
Choose the value that matches your design intent — baseline to reset, middle or top for common alignment needs, or a specific length or percentage for precise control.
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
<p style="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:
<p style="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:
<p style="display: none;">This text is completely removed from the layout.</p>
The white-space property is a shorthand that combines the behavior of white-space-collapse and text-wrap-mode. When you use a value that doesn't match any of the accepted keywords — whether due to a typo, a made-up value, or a value that belongs to a different CSS property — the W3C validator flags it as invalid. This commonly happens when authors confuse values from related properties (like using break-spaces where it isn't supported in inline styles being validated, or misspelling nowrap as no-wrap).
Using invalid CSS values means browsers will ignore the declaration entirely, falling back to the default behavior (white-space: normal). This can cause unexpected text wrapping or whitespace collapsing that breaks your layout. Keeping your CSS valid ensures consistent rendering across browsers and makes your stylesheets easier to maintain and debug.
The accepted values for white-space are:
normal— Collapses whitespace sequences, wraps text as needed (default).nowrap— Collapses whitespace but suppresses line breaks; text won't wrap.pre— Preserves whitespace and line breaks exactly as written, like the<pre>element.pre-wrap— Preserves whitespace and line breaks, but also allows text to wrap when necessary.pre-line— Collapses whitespace sequences into a single space, but preserves explicit line breaks and allows wrapping.break-spaces— Similar topre-wrap, but trailing spaces and spaces at the end of lines don't hang and do affect box sizing.
Additionally, CSS Text Level 4 introduced shorthand combinations using white-space-collapse and text-wrap-mode keywords, such as collapse, preserve, wrap, and preserve nowrap. However, support for these newer shorthand forms varies, and older validators or browsers may not recognize them.
Global CSS values (inherit, initial, revert, revert-layer, unset) are also valid.
Examples
Incorrect: invalid value triggers the error
A common mistake is using no-wrap (with a hyphen) instead of the correct nowrap:
<p style="white-space: no-wrap;">This text should not wrap.</p>
Another common mistake is using a value from a different property entirely:
<p style="white-space: hidden;">This text has an invalid white-space value.</p>
Correct: using valid white-space values
<p style="white-space: nowrap;">This text will not wrap to a new line.</p>
<p style="white-space: pre-wrap;">This text preserves whitespace
and line breaks, but also wraps when needed.</p>
<p style="white-space: pre-line;">This collapses extra spaces
but preserves explicit line breaks.</p>
Correct: using the property in a <style> block
<!DOCTYPE html>
<html lang="en">
<head>
<title>White-space example</title>
<style>
.no-wrap {
white-space: nowrap;
}
.preserve {
white-space: pre-wrap;
}
</style>
</head>
<body>
<p class="no-wrap">This long paragraph will stay on a single line without wrapping.</p>
<p class="preserve">This preserves multiple spaces
and line breaks exactly as written.</p>
</body>
</html>
If you encounter this validation error, double-check your white-space value for typos and confirm it matches one of the recognized keywords listed above.
This error originates from CSS validation, not HTML element validation. It typically appears when the validator encounters a width value in a style attribute or <style> block that doesn't conform to the CSS specification for the width property. The CSS width property accepts <length>, <percentage>, auto, min-content, max-content, fit-content, or fit-content(<length-percentage>) values. Anything outside these types—or an expression that produces an incompatible type—will trigger this error.
Common causes include:
- Missing units on a numeric value. In CSS,
width: 400is invalid (unlike the HTMLwidthattribute on elements like<img>, which expects a unitless integer). CSS requires a unit such aspx,em,rem,%,vw, etc., unless the value is0. - Invalid
calc()expressions. For example,calc(100% - 20)is invalid because100%is a percentage and20has no unit—you cannot subtract a unitless number from a percentage. It should becalc(100% - 20px). - Typos or unrecognized values. Things like
width: 50 px(space between number and unit),width: autopx, orwidth: 100pixelsare not valid CSS. - Using HTML attribute syntax in CSS. Writing
width: 400in a stylesheet because you're used to writing<img width="400">in HTML.
This matters for standards compliance and cross-browser reliability. While some browsers may attempt to interpret invalid values, the behavior is undefined and inconsistent. Relying on invalid CSS can lead to broken layouts in certain browsers or future browser versions.
How to Fix It
- Add proper units to any bare numeric
widthvalue in your CSS. Usepx,em,rem,%,vw, or another valid CSS length unit. - Check
calc()expressions to ensure both sides of addition or subtraction are compatible types (e.g., length with length, or percentage with length—both are valid incalc()). Unitless numbers (other than0) cannot be mixed with lengths or percentages in addition/subtraction. - Remove spaces between numbers and their units.
50pxis valid;50 pxis not. - Use valid keywords only:
auto,min-content,max-content,fit-content.
Examples
Incorrect: Missing unit in inline style
<div style="width: 400;">Content</div>
Correct: Adding a proper unit
<div style="width: 400px;">Content</div>
Incorrect: Incompatible types in calc()
<div style="width: calc(100% - 20);">Content</div>
The value 20 has no unit, so it cannot be subtracted from a percentage.
Correct: Compatible types in calc()
<div style="width: calc(100% - 20px);">Content</div>
Incorrect: Space between number and unit
<p style="width: 50 %;">Some text</p>
Correct: No space between number and unit
<p style="width: 50%;">Some text</p>
Incorrect: Unitless number in a <style> block
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example</title>
<style>
.sidebar {
width: 300;
}
</style>
</head>
<body>
<aside class="sidebar">Sidebar content</aside>
</body>
</html>
Correct: Valid length value in a <style> block
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example</title>
<style>
.sidebar {
width: 300px;
}
</style>
</head>
<body>
<aside class="sidebar">Sidebar content</aside>
</body>
</html>
Note on the HTML width attribute
Don't confuse CSS width with the HTML width attribute. The HTML width attribute on elements like <img>, <video>, and <canvas> expects a unitless integer:
<img src="photo.jpg" width="400" alt="A sample photo">
Writing width="400px" in an HTML attribute is a separate validation error. The CSS error discussed in this guide specifically concerns width values in stylesheets or style attributes where a valid CSS length is required.
The CSS width property contains an invalid value.
The width property accepts specific types of values: lengths (like 100px, 10em, 5rem), percentages (50%), viewport units (100vw), the keyword auto, and sizing keywords like max-content, min-content, or fit-content. The validator rejects anything that doesn't match these formats.
Common mistakes that trigger this error:
- Missing a unit:
width: 100instead ofwidth: 100px. Plain numbers (other than0) are not valid CSS lengths. - Using an invalid unit or typo:
width: 100 px(with a space),width: 100ppx. - Passing a non-length value:
width: red,width: bold,width: none. The keywordnoneworks formax-widthbut not forwidth. - Including extra characters:
width: 100px;50pxorwidth: 100px !importnt.
Invalid example
<div style="width: 600">
<p>This box has no unit on its width value.</p>
</div>
The value 600 is not valid because it lacks a CSS unit.
Fixed example
<div style="width: 600px">
<p>This box now has a valid width.</p>
</div>
Adding px (or another appropriate unit like em, %, rem, vw) makes the value valid. If the intent is to let the element size itself naturally, use width: auto instead.
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