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.
Unlike margin properties, which accept negative values to pull elements closer together or overlap them, all padding properties (padding-top, padding-right, padding-bottom, padding-left, and the padding shorthand) are defined in the CSS specification to only accept zero or positive lengths. This is because padding represents the space inside an element between its content and its border — a negative internal space is not a meaningful concept.
When you use a negative padding value, browsers will typically ignore the declaration entirely, meaning your layout may not look the way you intended. The W3C validator catches this to help you identify code that won’t behave consistently across browsers and doesn’t conform to the CSS specification.
If your goal is to reduce the space between elements, negative margin values are the correct tool. If you’re trying to shift content upward within a container, consider using position: relative with a negative top offset, or adjust the layout with other techniques like transform: translateY().
Examples
❌ Invalid: negative padding value
<div style="padding-top: -20px;">
This element has invalid negative padding.
</div>
The validator will report: CSS: “padding-top”: “-20” negative values are not allowed.
✅ Fixed: using zero or positive padding
<div style="padding-top: 0;">
This element has no top padding.
</div>
<div style="padding-top: 10px;">
This element has valid positive top padding.
</div>
✅ Alternative: using negative margin instead
If you need to reduce the space above an element, use a negative margin-top:
<div style="margin-top: -20px;">
This element is pulled upward with a negative margin.
</div>
❌ Invalid: negative values in the padding shorthand
The same rule applies to the padding shorthand property. Any negative component value is invalid:
<div style="padding: -10px 20px 15px 20px;">
Invalid shorthand padding.
</div>
✅ Fixed: all-positive shorthand values
<div style="padding: 0 20px 15px 20px;">
Valid shorthand padding with zero top padding.
</div>
✅ Alternative: using transform for visual offset
If you need to visually shift an element’s content upward without affecting layout flow, transform is a clean option:
<div style="transform: translateY(-20px);">
This element appears shifted upward.
</div>
A CSS parse error means the validator’s CSS parser encountered something unexpected and could not make sense of the code from that point forward. Unlike many HTML errors that pinpoint a specific rule violation, a parse error is more general — the parser simply gave up trying to interpret the CSS. This can sometimes cause a cascade of additional errors, since the parser may lose track of context after the initial failure.
This matters because browsers handle broken CSS unpredictably. While most browsers are forgiving and will skip invalid rules, the way they recover varies. Styles may render differently across browsers, or entire rule blocks may be silently ignored. Valid CSS ensures consistent rendering, easier debugging, and better maintainability.
Common causes of CSS parse errors include:
- Missing semicolons between declarations
- Unclosed curly braces { or extra closing braces }
- Unclosed comments (/* without a matching */)
- Invalid or empty property values (e.g., color: ;)
- Unexpected characters such as stray text, unsupported tokens, or HTML markup inside <style> blocks
- Typos in property names or values (e.g., colr: red)
- Using CSS syntax that isn’t valid in an attribute context, such as placing selectors inside a style attribute
To fix the error, go to the line indicated by the validator and carefully inspect the CSS around that point. Look for the common issues listed above. Sometimes the actual mistake is on a line before the reported one — for example, a missing semicolon on line 5 might only cause a parse error on line 6.
Examples
Missing semicolon
A missing semicolon causes the parser to misinterpret where one declaration ends and the next begins.
❌ Incorrect:
<p style="color: red font-size: 16px">Hello</p>
✅ Fixed:
<p style="color: red; font-size: 16px">Hello</p>
Unclosed curly brace
A missing closing brace causes the parser to treat subsequent rules as part of the unclosed block.
❌ Incorrect:
<style>
.container {
margin: 0 auto;
padding: 20px;
.title {
font-size: 24px;
}
</style>
✅ Fixed:
<style>
.container {
margin: 0 auto;
padding: 20px;
}
.title {
font-size: 24px;
}
</style>
Unclosed comment
A comment that is never closed causes everything after it to be consumed by the parser as part of the comment.
❌ Incorrect:
<style>
/* Set the main color
body {
color: #333;
}
</style>
✅ Fixed:
<style>
/* Set the main color */
body {
color: #333;
}
</style>
Empty or invalid property value
Declaring a property with no value or a clearly invalid value triggers a parse error.
❌ Incorrect:
<div style="background-color: ;">Content</div>
✅ Fixed:
<div style="background-color: #f0f0f0;">Content</div>
Selectors inside an inline style attribute
The style attribute only accepts declarations (property-value pairs), not selectors or full rule blocks.
❌ Incorrect:
<div style="p { color: blue; }">Content</div>
✅ Fixed:
<div style="color: blue;">Content</div>
Stray characters or typos
Unexpected characters anywhere in CSS will cause parsing to fail.
❌ Incorrect:
<style>
.box {
width: 100px;;
height: 50px
border: 1px solid #ccc;
}
</style>
While a double semicolon (;;) is technically harmless in most parsers, a missing semicolon after height: 50px merges it with the next line, producing an invalid value.
✅ Fixed:
<style>
.box {
width: 100px;
height: 50px;
border: 1px solid #ccc;
}
</style>
If the validator points to a line but the cause isn’t obvious, try isolating sections of your CSS and validating them separately using the W3C CSS Validation Service. This can help narrow down the exact location of the problem.
The pointer-events CSS property controls whether an element can be the target of pointer events such as clicks, taps, and hover states. Unlike many other CSS properties that accept normal as a keyword (e.g., white-space, letter-spacing), the pointer-events property does not include normal in its list of valid values. This is a common mistake since normal and auto are often used interchangeably across different CSS properties, but each property defines its own set of accepted keywords.
Using an invalid value like normal means the browser will ignore the entire declaration. In most cases this won’t cause visible breakage because the default behavior already allows pointer interaction, but it can lead to unexpected results if you’re relying on the property to override an inherited pointer-events: none from a parent element. The invalid declaration would simply be discarded, and the inherited none value would remain in effect, making the element unclickable.
For standard HTML elements, the two primary values you’ll use are:
- auto — The element behaves as it normally would regarding pointer events. This is the default.
- none — The element is never the target of pointer events. Clicks and hovers pass through to whatever is behind it.
Several additional values exist (visiblePainted, visibleFill, visibleStroke, visible, painted, fill, stroke, all) but these only apply to SVG elements. Global CSS keywords like inherit, initial, unset, and revert are also valid.
To fix the issue, replace normal with auto wherever it appears as a pointer-events value. If you’re using pointer-events: normal to restore default interactivity after a parent set pointer-events: none, then auto is exactly what you need.
Examples
Incorrect — using normal
<div style="pointer-events: normal;">
<a href="/about">About us</a>
</div>
The validator will flag normal as an invalid value for pointer-events.
Correct — using auto
<div style="pointer-events: auto;">
<a href="/about">About us</a>
</div>
Practical use case: restoring pointer events on a child
A common pattern is disabling pointer events on a parent and re-enabling them on a specific child. Using normal here would silently fail, leaving the button unclickable:
<!-- Incorrect -->
<style>
.overlay {
pointer-events: none;
}
.overlay .close-btn {
pointer-events: normal; /* Invalid — button remains unclickable */
}
</style>
<div class="overlay">
<button class="close-btn">Close</button>
</div>
<!-- Correct -->
<style>
.overlay {
pointer-events: none;
}
.overlay .close-btn {
pointer-events: auto; /* Valid — button is clickable again */
}
</style>
<div class="overlay">
<button class="close-btn">Close</button>
</div>
In the incorrect version, the browser discards the invalid pointer-events: normal declaration entirely, so the .close-btn inherits none from the parent and cannot be clicked. Changing it to auto correctly restores interactivity on the button.
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 0 without a unit for zero values — The value 0 is valid on its own and doesn’t require a unit.
- Use a valid keyword — Properties like width accept keywords such as auto, min-content, max-content, and fit-content.
- Check dynamic values — If a preprocessor or template engine generates the value, verify it outputs a complete length (e.g., ${value}px where value is not empty).
Examples
Incorrect: unit without a number
<style>
.box {
width: px;
}
</style>
<div class="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>
<div class="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>
<div class="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.
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
<div style="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
<div style="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 stroke-width property controls the thickness of the outline (stroke) drawn around shapes and text, primarily used in SVG but also applicable to HTML elements via CSS. According to both the SVG specification and the CSS standard, stroke-width accepts only non-negative values — that is, zero or any positive number, optionally with a CSS length unit like px, em, or rem. A unitless number is also valid and is interpreted in the current coordinate system’s user units.
Negative values are logically meaningless for stroke width because you cannot draw an outline with negative thickness. Browsers will typically ignore or discard the invalid declaration, meaning the stroke may render with an unexpected default width or not at all. Beyond rendering issues, using invalid CSS values causes W3C validation errors, which can indicate broader quality problems in your code and may lead to unpredictable behavior across different browsers.
A common cause of this error is dynamic value generation — for example, a CSS calc() expression or a preprocessor variable that inadvertently produces a negative result. If your stroke width is computed, make sure to clamp the value so it never goes below 0.
How to fix it
- Replace negative values with 0 or a positive number. If you intended no visible stroke, use 0. If you wanted a visible stroke, use the appropriate positive thickness.
- Guard computed values. If the value comes from a calc() expression or CSS custom property, use max() to ensure the result is never negative — for example, stroke-width: max(0px, calc(10px - 15px)).
- Check inline styles and stylesheets. The error can appear in both inline style attributes and external/internal CSS. Search your codebase for any stroke-width declaration with a negative number.
Examples
❌ Invalid: negative stroke-width on an HTML element
<p style="stroke-width: -1">Some content</p>
This triggers the validator error because -1 is not an allowed value.
✅ Fixed: non-negative stroke-width
<p style="stroke-width: 0">Some content</p>
Using 0 removes the stroke entirely and is valid.
❌ Invalid: negative stroke-width on an SVG element
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="-3" fill="none"/>
</svg>
✅ Fixed: positive stroke-width on an SVG element
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="none"/>
</svg>
✅ Using max() to clamp a computed value
<div style="stroke-width: max(0px, calc(5px - 10px))">Some content</div>
Here, calc(5px - 10px) would produce -5px, but max(0px, ...) ensures the final value is 0px, keeping the CSS valid.
The text-align CSS property controls horizontal alignment of inline-level content within a block element. When the W3C HTML validator encounters an inline style attribute containing a text-align value it doesn’t recognize, it flags the error with a message like CSS: “text-align”: X is not a “text-align” value, where X is the offending value.
This error commonly occurs for a few reasons:
- Confusing text-align with vertical-align: Using middle, top, or bottom — these are vertical-align values, not text-align values.
- Typos: Writing cetner instead of center, or rigth instead of right.
- Using non-standard values: Trying values like auto, none, or arbitrary strings that aren’t part of the specification.
- Confusing CSS properties: Using Flexbox or Grid alignment values like flex-start or space-between with text-align.
While most browsers will silently ignore an invalid text-align value and fall back to the inherited or default alignment, relying on this behavior is problematic. It makes your intent unclear, can lead to inconsistent rendering, and signals that there may be a deeper misunderstanding in your styling approach. Valid CSS ensures predictable behavior across all browsers and assistive technologies.
Valid values for text-align
| Value | Description |
|---|---|
| left | Aligns content to the left edge |
| right | Aligns content to the right edge |
| center | Centers the content horizontally |
| justify | Stretches content to fill the full width |
| start | Aligns to the start edge (direction-aware) |
| end | Aligns to the end edge (direction-aware) |
The start and end values are logical properties that respect the document’s writing direction (dir attribute or direction CSS property), making them ideal for internationalized content.
Examples
Invalid: using middle instead of center
A common mistake is using middle, which is a valid value for vertical-align but not for text-align:
<p style="text-align: middle;">This text will fail validation.</p>
Fix: Replace middle with center:
<p style="text-align: center;">This text is properly centered.</p>
Invalid: typo in the value
<h2 style="text-align: cetner;">Heading</h2>
Fix: Correct the spelling:
<h2 style="text-align: center;">Heading</h2>
Invalid: using a non-existent value
<div style="text-align: auto;">Some content</div>
Fix: Choose a valid alignment value:
<div style="text-align: left;">Some content</div>
Invalid: using a vertical alignment value
<p style="text-align: top;">Paragraph text</p>
Fix: If you intended horizontal alignment, use a valid text-align value. If you actually need vertical positioning, use vertical-align on an inline or table-cell element instead:
<p style="text-align: left;">Paragraph text</p>
Valid examples showing all common values
<p style="text-align: left;">Left-aligned text.</p>
<p style="text-align: right;">Right-aligned text.</p>
<p style="text-align: center;">Centered text.</p>
<p style="text-align: justify;">Justified text stretches to fill the full width of its container.</p>
<p style="text-align: start;">Start-aligned (respects text direction).</p>
<p style="text-align: end;">End-aligned (respects text direction).</p>
When fixing this error, double-check which property you actually need. If you want to center a block-level element itself (not its text content), text-align isn’t the right tool — consider using margin: 0 auto or Flexbox instead. The text-align property is specifically for the horizontal alignment of inline content within its containing block.
The text-overflow CSS property controls how overflowed content that is not displayed is signaled to users. It applies when an element’s overflow is hidden (e.g., overflow: hidden) and the content exceeds the element’s box. The property accepts specific values, and using anything outside the allowed set — such as a misspelled keyword, an unquoted string, or a made-up value — will trigger this validation error.
Accepted values
The text-overflow property accepts the following values:
- clip — Truncates the text at the edge of the content area. Characters may be clipped mid-glyph. This is the default.
- ellipsis — Displays an ellipsis character (…) to indicate clipped text.
- A custom <string> — A quoted string to display at the clipping point (e.g., " [..]"). Note that browser support for custom strings is limited.
- Global CSS values — inherit, initial, revert, revert-layer, and unset.
The property can take one or two values. If one value is given, it specifies the overflow behavior for the end of the line (the right end for left-to-right text, the left end for right-to-left text). If two values are given, the first controls the left end of the line and the second controls the right end. Two-value syntax has limited browser support.
Common mistakes that trigger this error include:
- Misspelling ellipsis (e.g., elipsis, ellipses).
- Using a value from a different property (e.g., hidden, scroll, auto).
- Using an unquoted custom string instead of a properly quoted one.
- Using a numeric or length value (e.g., 10px), which is not valid for this property.
Why this matters
Invalid CSS values are ignored by browsers, which means the property will fall back to its default (clip) instead of behaving as you intended. This can lead to text being abruptly cut off without any visual indicator, harming readability and user experience. Fixing validation errors also ensures your stylesheets are clean, predictable, and maintainable.
Examples
Incorrect — misspelled keyword
/* "elipsis" is not a valid text-overflow value */
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: elipsis;
}
Incorrect — value from another property
/* "hidden" is an overflow value, not a text-overflow value */
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: hidden;
}
Incorrect — unquoted custom string
/* Custom strings must be quoted */
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: [more];
}
Correct — using ellipsis
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
Correct — using clip (the default)
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: clip;
}
Correct — using a quoted custom string
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: " [..]";
}
Correct — two-value syntax
/* Left end uses ellipsis, right end uses a custom string */
.truncated {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis " [..]";
}
Note that text-overflow on its own does not force overflow to occur. To make text actually truncate, you typically need overflow: hidden (or another non-visible overflow value) and white-space: nowrap on the element. The text-overflow property only controls how the clipped content is signaled visually.
The text-transform CSS property controls the capitalization of text within an element. It’s commonly used to enforce consistent text casing — for example, making headings appear in all uppercase or ensuring navigation links are lowercase — without changing the actual content in the HTML. When the validator encounters a value it doesn’t recognize for this property, it flags it as invalid.
This error can occur for several reasons:
- Typos — writing upppercase instead of uppercase, or Capitalize instead of capitalize (CSS values are case-sensitive in validation contexts).
- Incorrect values — using values from other properties, like bold, italic, or center, which don’t apply to text-transform.
- Non-standard values — using browser-specific or experimental values that aren’t part of the CSS specification.
- Wrong property — confusing text-transform with text-decoration, text-align, or font-variant, and using their values here instead.
Fixing this matters because invalid CSS can lead to unpredictable rendering across browsers. While most browsers will simply ignore an invalid declaration, your intended styling won’t be applied, potentially breaking your design. Keeping your CSS valid also improves maintainability and ensures forward compatibility.
Valid values for text-transform
| Value | Effect |
|---|---|
| none | No capitalization change (default) |
| capitalize | First letter of each word is uppercased |
| uppercase | All characters are converted to uppercase |
| lowercase | All characters are converted to lowercase |
| full-width | Forces characters into a full-width form (useful for CJK typography) |
| full-size-kana | Converts small kana characters to full-size equivalents |
Examples
Incorrect — invalid value
In this example, bold is not a valid text-transform value. It likely belongs on the font-weight property instead.
<p style="text-transform: bold;">Welcome to our site</p>
Similarly, a simple typo will trigger this error:
<p style="text-transform: uppercse;">Welcome to our site</p>
Correct — using valid values
<p style="text-transform: uppercase;">Welcome to our site</p>
<p style="text-transform: capitalize;">Welcome to our site</p>
Correct — separating concerns with the right properties
If you intended to make text bold and uppercase, use the appropriate property for each effect:
<p style="font-weight: bold; text-transform: uppercase;">Welcome to our site</p>
Correct — using text-transform in a stylesheet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Text Transform Example</title>
<style>
.heading {
text-transform: uppercase;
}
.name {
text-transform: capitalize;
}
.code-snippet {
text-transform: none;
}
</style>
</head>
<body>
<h1 class="heading">site navigation</h1>
<p class="name">john doe</p>
<code class="code-snippet">myVariable</code>
</body>
</html>
If you’re unsure which value you need, uppercase and capitalize are the most commonly used. Use none when you need to override a text-transform rule inherited from a parent element.
The word-break property controls how words break when they would overflow their container. While break-word was historically used as a value for this property, the CSS Text Module Level 3 specification has deprecated it. As the spec explains:
For compatibility with legacy content, the word-break property also supports a deprecated break-word keyword. When specified, this has the same effect as word-break: normal and overflow-wrap: anywhere, regardless of the actual value of the overflow-wrap property.
Browsers still support word-break: break-word for backward compatibility, but relying on deprecated values is risky. Future browser versions may remove support, and validators will flag it as an issue. The correct approach is to use the overflow-wrap property instead, which is specifically designed to control whether and how words break when they overflow their container.
There are two modern alternatives to consider:
- overflow-wrap: break-word — allows the browser to break an otherwise unbreakable word at an arbitrary point to prevent overflow. The word only breaks if it cannot fit on its own line. This is the most widely supported option.
- overflow-wrap: anywhere — works like break-word but also allows the broken word fragments to be considered during min-content sizing calculations. This means containers using min-content or fit-content widths can shrink further.
In most cases, overflow-wrap: break-word is the right replacement.
Examples
Deprecated usage
This triggers the W3C CSS validation warning:
<div style="word-break: break-word;">
Thisislongtext_thatdoesnotcontainanyspaces_andshouldbreakproperly
</div>
Fixed with overflow-wrap: break-word
This is the most common and well-supported fix:
<div style="word-break: normal; overflow-wrap: break-word;">
Thisislongtext_thatdoesnotcontainanyspaces_andshouldbreakproperly
</div>
Fixed with overflow-wrap: anywhere
Use this if you also want min-content sizing to account for the broken fragments:
<div style="word-break: normal; overflow-wrap: anywhere;">
Thisislongtext_thatdoesnotcontainanyspaces_andshouldbreakproperly
</div>
In a stylesheet
If the deprecated value appears in a CSS file or <style> block, apply the same fix:
<style>
/* Deprecated */
/*
.content {
word-break: break-word;
}
*/
/* Correct */
.content {
word-break: normal;
overflow-wrap: break-word;
}
</style>
<div class="content">
Thisislongtext_thatdoesnotcontainanyspaces_andshouldbreakproperly
</div>
Understanding the difference between overflow-wrap values
<style>
.container {
width: min-content;
border: 1px solid black;
margin-bottom: 1rem;
}
.wrap-break-word {
overflow-wrap: break-word;
}
.wrap-anywhere {
overflow-wrap: anywhere;
}
</style>
<!-- With break-word, min-content width is based on the longest word -->
<div class="container wrap-break-word">
Short words here but_a_very_long_unbreakable_word_too
</div>
<!-- With anywhere, min-content can shrink past even long words -->
<div class="container wrap-anywhere">
Short words here but_a_very_long_unbreakable_word_too
</div>
With overflow-wrap: break-word, the container’s min-content width will be determined by the longest unbreakable string. With overflow-wrap: anywhere, even that long string can be broken, allowing the container to shrink further. For most use cases where you simply want to prevent text from overflowing a fixed-width container, overflow-wrap: break-word is the straightforward choice.
A percentage value in your CSS exceeds the allowed range of 0 to 100.
CSS properties that accept percentage values — such as opacity, width, height, and others used in certain contexts — may be restricted to specific ranges. When you embed CSS in an HTML document (via the style attribute or a <style> element), the W3C HTML Validator checks these values and flags any that fall outside the permitted range.
A value like 100.01% is just slightly over the maximum of 100%. This is often a typo or a rounding error. While most browsers will silently clamp the value to 100%, it is still invalid and should be corrected.
HTML Examples
❌ Invalid: value out of range
<div style="width: 100.01%;">Content</div>
✅ Fixed: value within range
<div style="width: 100%;">Content</div>
The transform CSS property lets you rotate, scale, skew, or translate an element by modifying its coordinate space. The W3C validator raises this error when the value assigned to transform doesn’t conform to valid CSS syntax. This typically happens when:
- A transform function name is misspelled (e.g., rotateZ typed as rotatez in some contexts, or skew typed as skeew).
- Too many arguments are passed to a transform function (e.g., rotate(45deg, 20deg) instead of rotate(45deg)).
- Arguments are missing required units (e.g., rotate(45) instead of rotate(45deg)).
- Multiple transform functions are separated by commas instead of spaces.
- An invalid or non-existent function name is used (e.g., transform: flip()).
- Vendor-prefixed values like -webkit-transform syntax are used in the standard transform property incorrectly.
This matters for standards compliance because browsers may silently ignore an invalid transform declaration entirely, meaning none of your intended transformations will be applied. Catching these errors during validation helps prevent unexpected layout or visual issues.
Each transform function has a specific signature. For example, rotate() accepts exactly one angle value, translate() accepts one or two length/percentage values, and scale() accepts one or two numbers. Providing the wrong number or type of arguments triggers this error.
Examples
Incorrect: Comma-separated transform functions
Multiple transforms must be space-separated, not comma-separated.
<div style="transform: rotate(45deg), scale(1.5);">Transformed</div>
Correct: Space-separated transform functions
<div style="transform: rotate(45deg) scale(1.5);">Transformed</div>
Incorrect: Missing unit on rotation value
The rotate() function requires an angle unit such as deg, rad, grad, or turn.
<div style="transform: rotate(45);">Rotated</div>
Correct: Angle value with unit
<div style="transform: rotate(45deg);">Rotated</div>
Incorrect: Too many arguments in a function
The rotate() function accepts only one argument.
<div style="transform: rotate(45deg, 20deg);">Rotated</div>
Correct: Single argument for rotate()
If you need to rotate around a specific axis, use rotateX(), rotateY(), or rotateZ() instead.
<div style="transform: rotateZ(45deg);">Rotated on Z axis</div>
Incorrect: Misspelled or non-existent function
<div style="transform: roate(30deg) scaleX(2);">Transformed</div>
Correct: Properly spelled function names
<div style="transform: rotate(30deg) scaleX(2);">Transformed</div>
Incorrect: Using translate without units on non-zero lengths
<div style="transform: translate(50, 100);">Moved</div>
Correct: Length values with units
A value of 0 does not require a unit, but all other length values do.
<div style="transform: translate(50px, 100px);">Moved</div>
Valid Transform Functions Reference
Here are the commonly used transform functions and their expected arguments:
- translate(tx) or translate(tx, ty) — lengths or percentages
- translateX(tx), translateY(ty), translateZ(tz) — a single length/percentage
- scale(sx) or scale(sx, sy) — unitless numbers
- scaleX(sx), scaleY(sy), scaleZ(sz) — a single unitless number
- rotate(angle) — a single angle value (e.g., 45deg)
- rotateX(angle), rotateY(angle), rotateZ(angle) — a single angle
- skew(ax) or skew(ax, ay) — angle values
- skewX(ax), skewY(ay) — a single angle
- matrix(a, b, c, d, tx, ty) — exactly six unitless numbers
- matrix3d(...) — exactly sixteen unitless numbers
When combining multiple transforms, always separate them with spaces and verify each function’s name and argument count against the specification.
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 of rotate(45deg))
- Typos in function names (e.g., rotatee(10deg) or tranlate(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.
<div style="transform: rotate(45);">Rotated text</div>
Fixed: adding the angle unit
<div style="transform: rotate(45deg);">Rotated text</div>
Invalid: typo in function name
<div style="transform: tranlateX(10px);">Shifted text</div>
Fixed: correcting the function name
<div style="transform: translateX(10px);">Shifted text</div>
Invalid: using a non-transform value
A plain number or unrelated keyword is not a valid transform value.
<div style="transform: 200px;">Content</div>
Fixed: using a proper transform function
<div style="transform: translateX(200px);">Content</div>
Invalid: wrong number of arguments for matrix()
The matrix() function requires exactly six comma-separated numbers.
<div style="transform: matrix(1, 2, 3);">Content</div>
Fixed: providing all six arguments
<div style="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.
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 → 0s or 0ms
- Check both longhand properties (transition-delay, transition-duration) and the transition shorthand.
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.
The ::file-selector-button pseudo-element targets the button inside an <input type="file"> element — the part users click to open the file picker dialog. It is supported in all major modern browsers and is part of the CSS Pseudo-Elements Level 4 specification.
The W3C CSS Validator has not yet been updated to recognize ::file-selector-button, which causes it to flag the pseudo-element as invalid. This is a known bug that has been reported on GitHub. Because the validator’s CSS grammar definitions lag behind the living standards, certain newer — but fully standardized — features trigger false positives.
Before ::file-selector-button was standardized, browsers used vendor-prefixed versions like ::-webkit-file-upload-button and ::-ms-browse. The unprefixed ::file-selector-button replaced these and is now the recommended approach.
What you should do
Since this is a validator limitation rather than an actual code issue, you can safely ignore this warning. Your CSS is standards-compliant. If you want to suppress the warning in a CI/CD pipeline or validation report, you can add a comment noting the false positive, but do not remove or change the pseudo-element — it is correct.
Browser support
::file-selector-button is supported in Chrome 89+, Firefox 82+, Safari 14.1+, and Edge 89+. For older browsers, you may include the vendor-prefixed versions as fallbacks, though these will also trigger validator warnings.
Examples
CSS that triggers the validator warning
::file-selector-button {
background-color: #4a90d9;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
}
This CSS is valid and correct despite the validator warning. It styles the file selector button with a custom background color, text color, and border radius.
Scoping to a specific input
input[type="file"]::file-selector-button {
background-color: #4a90d9;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
}
input[type="file"]::file-selector-button:hover {
background-color: #357abd;
}
Including vendor-prefixed fallbacks for older browsers
input[type="file"]::-webkit-file-upload-button {
background-color: #4a90d9;
color: white;
border: none;
padding: 8px 16px;
}
input[type="file"]::file-selector-button {
background-color: #4a90d9;
color: white;
border: none;
padding: 8px 16px;
}
The vendor-prefixed version is placed first so that the standard ::file-selector-button rule takes precedence in browsers that support both. Note that the vendor-prefixed versions will also trigger validator warnings for the same reason.
Corresponding HTML
<label for="upload">Choose a file:</label>
<input type="file" id="upload" name="upload">
There is nothing to fix in your HTML or CSS. This is purely a validator false positive that will be resolved when the W3C Validator updates its CSS grammar definitions.
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.
::ng-deep is a pseudo-class combinator specific to Angular’s view encapsulation system. It tells Angular’s compiler to disable style encapsulation for a particular CSS rule, allowing that rule to penetrate into child component views. Because it is not defined in any W3C or WHATWG CSS specification, the CSS validator flags it as an unknown pseudo-element or pseudo-class.
This is important to understand for several reasons:
- It’s not a standard CSS feature. No browser natively understands ::ng-deep. Angular’s build toolchain processes and removes it at compile time, rewriting the selector before it reaches the browser. The validator checks against CSS specifications and rightfully does not recognize it.
- It’s deprecated within Angular itself. The Angular team has deprecated ::ng-deep and plans to remove it in a future version. Continued use ties your codebase to a feature with no long-term support.
- It can cause unintended global style leakage. When combined with :host improperly or used without care, ::ng-deep can bleed styles into components where they weren’t intended, making your application harder to maintain.
How to Fix It
There are several approaches depending on your situation:
1. Accept the Validation Warning
If you’re working in an Angular project and need ::ng-deep temporarily, you can acknowledge this as a known framework-specific warning. The validator is technically correct — the selector isn’t valid CSS — but Angular’s compiler handles it before it reaches the browser.
2. Use Global Stylesheets
Move the styles that need to cross component boundaries into a global stylesheet (like styles.css). Global styles are not encapsulated and naturally apply to all components.
3. Set ViewEncapsulation to None
Disable view encapsulation on the component that needs to style its children. This makes all of that component’s styles global in scope, removing the need for ::ng-deep.
4. Use CSS Custom Properties (Recommended)
The most standards-compliant approach is to use CSS custom properties (variables) to create a theming API for your components. Custom properties naturally inherit through the DOM tree, crossing shadow DOM and Angular encapsulation boundaries.
Examples
❌ Using ::ng-deep (Triggers the Validation Warning)
:host ::ng-deep .child-button {
background-color: blue;
color: white;
}
The validator does not recognize ::ng-deep and reports the error.
✅ Using a Global Stylesheet Instead
In your global styles.css file, target the element with a specific class or selector:
app-parent .child-button {
background-color: blue;
color: white;
}
This avoids ::ng-deep entirely by placing the rule outside of component-scoped styles.
✅ Using CSS Custom Properties
Define customizable properties in the child component’s CSS:
/* Child component styles */
.child-button {
background-color: var(--child-button-bg, gray);
color: var(--child-button-color, black);
}
Then set the values from the parent component’s CSS:
/* Parent component styles */
:host {
--child-button-bg: blue;
--child-button-color: white;
}
This approach is fully standards-compliant, passes CSS validation, and creates a clean styling API between components. CSS custom properties inherit naturally through the DOM, so they work across component boundaries without breaking encapsulation.
✅ Using ::part() for Web Components
If your child components use shadow DOM (or you’re migrating toward web components), the standard ::part() pseudo-element lets you expose specific elements for external styling:
/* Parent styles targeting an exposed part */
child-component::part(button) {
background-color: blue;
color: white;
}
The ::part() pseudo-element is a W3C standard and fully recognized by the CSS validator.
CSS pseudo-classes (like :hover, :focus, :nth-child()) select elements based on their state or position, while pseudo-elements (like ::before, ::after, ::placeholder) target specific parts of an element. The CSS specification distinguishes between the two by using a single colon for pseudo-classes and a double colon for pseudo-elements. While browsers still support the legacy single-colon syntax for older pseudo-elements like :before and :after for backward compatibility, the validator expects the modern double-colon form ::before and ::after.
Why This Matters
Standards compliance. The double-colon syntax for pseudo-elements was introduced in CSS3 to clearly distinguish pseudo-elements from pseudo-classes. Using the correct syntax makes your code more readable and future-proof.
Catching real bugs. A misspelled pseudo-class like :foucs or :hovr will silently fail — the browser simply ignores the entire rule. The validator catches these typos before they cause mysterious styling issues in production.
Validator profile limitations. The W3C CSS validator checks your styles against a specific CSS profile. Newer selectors like :has(), :is(), or :where() may not be recognized if the validator is set to an older profile like CSS Level 2.1 or even CSS Level 3. Vendor-prefixed selectors like ::-webkit-input-placeholder or ::-moz-placeholder are never part of any standard profile and will always be flagged.
Common Causes
- Typos — :hovr, :foucs, ::plceholder, etc.
- Single colon on pseudo-elements — :before, :after, :first-line, :first-letter instead of their double-colon equivalents.
- Vendor-prefixed selectors — ::-webkit-input-placeholder, ::-moz-selection, :-ms-input-placeholder.
- Modern selectors on older profiles — :has(), :is(), :where(), ::marker, :focus-visible may not be recognized depending on the validator’s CSS level setting.
How to Fix
- Check spelling of all pseudo-classes and pseudo-elements.
- Use double colons for pseudo-elements: ::before, ::after, ::first-line, ::first-letter, ::placeholder, ::marker, ::selection.
- Replace vendor-prefixed selectors with their standard equivalents. If you still need the prefix for browser support, place the standard version alongside it and accept that the prefixed line may produce a warning.
- Update the validator profile to a newer CSS level if you’re intentionally using modern selectors like :has() or :focus-visible.
Examples
Incorrect — triggers the warning
<style>
/* Typo in pseudo-class */
a:hovr {
color: red;
}
/* Single colon on pseudo-element */
p:before {
content: "→ ";
}
/* Vendor-prefixed pseudo-element without standard version */
input::-webkit-input-placeholder {
color: gray;
}
</style>
Each of these rules will trigger an “Unknown pseudo-element or pseudo-class” warning. The first is a simple typo, the second uses outdated single-colon syntax, and the third is a non-standard vendor prefix.
Correct — valid CSS
<style>
/* Fixed typo */
a:hover {
color: red;
}
/* Double colon for pseudo-element */
p::before {
content: "→ ";
}
/* Standard pseudo-element */
input::placeholder {
color: gray;
}
</style>
Handling modern selectors
Some modern pseudo-classes like :has() and :focus-visible are well-supported in browsers but may not yet be recognized by the validator. If you need to use them, you can acknowledge the warning or structure your CSS so the modern selector enhances rather than replaces base styles:
<style>
/* Base style that always applies */
.card {
border: 1px solid transparent;
}
/* Enhancement using :has() — may warn in the validator */
.card:has(img) {
border-color: #ccc;
}
/* :focus-visible for keyboard-only focus rings */
button:focus-visible {
outline: 2px solid blue;
}
</style>
These selectors are valid CSS and work in modern browsers. If the validator flags them, consider switching the validator’s profile to the latest CSS level, or treat the warnings as informational rather than errors.
The @container at-rule enables container queries, allowing you to apply styles to elements based on the size or inline-size of a parent container rather than the viewport. It was introduced as part of the CSS Containment Module Level 3 specification and has been supported in all major browsers (Chrome, Edge, Firefox, Safari) since early 2023.
The W3C CSS validator uses its own CSS parsing engine, which sometimes lags behind the latest CSS specifications. Because @container is relatively new compared to long-established at-rules like @media or @keyframes, the validator may flag it as unrecognized. This does not mean your CSS is invalid or broken — it simply means the validator hasn’t caught up with the spec yet.
Why This Warning Appears
The W3C validator checks your CSS against known grammar rules. When it encounters an at-rule it doesn’t have in its internal dictionary, it reports it as unrecognized. Other modern CSS features like @layer and @property have historically triggered the same kind of false positive before being added to the validator’s parser.
Since this is a validator limitation and not an actual code issue, there’s no required “fix.” However, you should still make sure your @container usage is syntactically correct and that you’ve properly set up containment on the parent element.
How Container Queries Work
For @container to function, a parent element must be designated as a containment context using the container-type property (or the container shorthand). Without this, the browser won’t know which ancestor to query against.
Examples
Correct usage of @container
The parent element needs container-type set to inline-size or size so its descendants can query against it:
<div class="card-wrapper">
<div class="card">
<h2>Title</h2>
<p>Some content here.</p>
</div>
</div>
.card-wrapper {
container-type: inline-size;
container-name: card-container;
}
@container card-container (min-width: 400px) {
.card {
display: flex;
gap: 1rem;
}
}
In this example, .card-wrapper is established as a containment context. When its inline size is at least 400px, the .card inside switches to a flex layout. The validator may flag the @container block, but this CSS is perfectly valid and works in all modern browsers.
Using @container without a named container
You can omit the container name, and the query will match the nearest ancestor with a container-type set:
.sidebar {
container-type: inline-size;
}
@container (max-width: 300px) {
.sidebar-nav {
flex-direction: column;
}
}
Common mistake: forgetting container-type
If you use @container without declaring a containment context on a parent, the query will never match and your styles won’t apply. This won’t cause a validator error, but it’s a logical bug:
/* Missing container-type — the @container query has nothing to query against */
.wrapper {
max-width: 600px;
}
@container (min-width: 400px) {
.content {
font-size: 1.25rem;
}
}
The fix is to add container-type to the intended parent:
.wrapper {
max-width: 600px;
container-type: inline-size;
}
@container (min-width: 400px) {
.content {
font-size: 1.25rem;
}
}
What You Should Do
- Verify your syntax — make sure you’re using container-type on a parent element and that your @container query follows the correct grammar.
- Ignore the validator warning — this is a known limitation of the W3C CSS validator. Your CSS is valid per the specification.
- Check browser support — @container is supported in Chrome 105+, Edge 105+, Firefox 110+, and Safari 16+. If you need to support older browsers, consider using @container as a progressive enhancement alongside a fallback layout.
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 .css file 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 @tailwind directive is used in Tailwind CSS source files to inject Tailwind’s generated styles into your stylesheet. Common usages include @tailwind base;, @tailwind components;, and @tailwind utilities;. These directives act as placeholders that Tailwind’s build tool (such as the Tailwind CLI or PostCSS plugin) replaces with actual CSS rules during compilation.
The W3C CSS Validator checks stylesheets against official CSS specifications maintained by the W3C. Since @tailwind is not defined in any CSS specification — it’s a framework-specific extension — the validator correctly flags it as unrecognized. This doesn’t mean your CSS is broken or will cause problems in browsers; it simply means the validator doesn’t know what @tailwind is.
This distinction matters because the @tailwind directives should never appear in production CSS served to browsers. If you’re seeing this validation error on a live website, it could indicate one of two things: either you’re linking directly to your uncompiled Tailwind source file (which is a problem), or the validator is checking your source CSS rather than the compiled output.
How to Fix It
Ensure you’re serving compiled CSS
The most important step is to make sure your build pipeline is correctly processing your Tailwind source files. The compiled output should contain only standard CSS — no @tailwind directives. Verify that your <link> tags point to the compiled CSS file, not the source file.
Use the Tailwind CDN or CLI correctly
If you’re using the Tailwind CLI, run the build command to generate a compiled stylesheet and reference that file in your HTML. The compiled file will contain only valid CSS.
Consider suppressing the warning
If the validator is checking your source CSS (for example, inline <style> blocks that get processed client-side), and you’re confident the directives are being handled correctly, you can safely suppress or ignore this specific warning in your validation reports.
Examples
Source CSS that triggers the warning
This is a typical Tailwind CSS source file that the validator will flag:
@tailwind base;
@tailwind components;
@tailwind utilities;
.custom-button {
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
Each @tailwind line will produce an “Unrecognized at-rule” warning from the W3C Validator.
Correct approach: serve compiled CSS
After running the Tailwind build process, the output file contains only standard CSS:
*, ::before, ::after {
box-sizing: border-box;
}
.custom-button {
padding: 0.5rem 1rem;
border-radius: 0.25rem;
}
.flex {
display: flex;
}
.text-center {
text-align: center;
}
Reference this compiled file in your HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
<link rel="stylesheet" href="css/output.css">
</head>
<body>
<p class="text-center">Hello, world!</p>
</body>
</html>
Avoid linking to uncompiled source files
This is incorrect — it links directly to the source file containing @tailwind directives:
<!-- Don't do this -->
<link rel="stylesheet" href="css/input.css">
Instead, always link to the build output:
<!-- Do this -->
<link rel="stylesheet" href="css/output.css">
By ensuring your production site serves only the compiled CSS output, you eliminate the @tailwind at-rule warnings entirely and deliver valid, standards-compliant stylesheets to your users.
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 subscript
- super — aligns as a superscript
- text-top — aligns with the top of the parent’s font
- text-bottom — aligns with the bottom of the parent’s font
- middle — aligns the middle of the element with the baseline plus half the x-height of the parent
- top — aligns the top of the element with the top of the tallest element on the line
- bottom — 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.
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 to pre-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: 400 is invalid (unlike the HTML width attribute on elements like <img>, which expects a unitless integer). CSS requires a unit such as px, em, rem, %, vw, etc., unless the value is 0.
- Invalid calc() expressions. For example, calc(100% - 20) is invalid because 100% is a percentage and 20 has no unit—you cannot subtract a unitless number from a percentage. It should be calc(100% - 20px).
- Typos or unrecognized values. Things like width: 50 px (space between number and unit), width: autopx, or width: 100pixels are not valid CSS.
- Using HTML attribute syntax in CSS. Writing width: 400 in 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 width value in your CSS. Use px, 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 in calc()). Unitless numbers (other than 0) cannot be mixed with lengths or percentages in addition/subtraction.
- Remove spaces between numbers and their units. 50px is valid; 50 px is 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 sets an element’s width and accepts a single value. The W3C validator reports this error when it encounters something it cannot parse as a valid width declaration. Common causes include:
- Missing units: Writing width: 300 instead of width: 300px. CSS requires explicit units for non-zero lengths.
- Multiple values: Writing width: 100px 200px as if width accepted shorthand-style multiple values (it doesn’t).
- Typos or invalid keywords: Writing width: auot instead of width: auto, or using a made-up keyword.
- Invalid functions or syntax: Using incorrect function syntax like width: calc(100% - 20px) with missing spaces around operators, or using browser-prefixed values without a standard fallback.
- Unsupported values: Using newer CSS values like fit-content or max-content in a context where the validator’s CSS level doesn’t recognize them.
This matters because invalid CSS can cause browsers to silently discard the entire declaration, meaning your intended layout won’t be applied. Different browsers may handle invalid values differently, leading to inconsistent rendering. Keeping your CSS valid ensures predictable, cross-browser behavior.
Valid values for width
The width property accepts exactly one of the following:
- Length values: A number with a unit, such as 300px, 25em, 10rem, 5vw.
- Percentage values: A percentage relative to the containing block, such as 75%.
- Keyword values: auto, max-content, min-content, fit-content.
- Function values: fit-content(20em), calc(100% - 40px), min(300px, 100%), max(200px, 50%), clamp(200px, 50%, 600px).
- Global values: inherit, initial, revert, revert-layer, unset.
Note that 0 is the only numeric value that does not require a unit.
Examples
Incorrect: missing unit
<style>
.box {
width: 300;
}
</style>
<div class="box">Content</div>
A bare number (other than 0) is not valid. The browser won’t know if you mean pixels, ems, or something else.
Correct: unit provided
<style>
.box {
width: 300px;
}
</style>
<div class="box">Content</div>
Incorrect: too many values
<style>
.sidebar {
width: 200px 400px;
}
</style>
<aside class="sidebar">Sidebar</aside>
Unlike properties such as margin or padding, width only accepts a single value.
Correct: single value
<style>
.sidebar {
width: 200px;
}
</style>
<aside class="sidebar">Sidebar</aside>
Incorrect: typo in keyword
<style>
.container {
width: auot;
}
</style>
<div class="container">Content</div>
Correct: proper keyword
<style>
.container {
width: auto;
}
</style>
<div class="container">Content</div>
Incorrect: malformed calc() expression
<style>
.panel {
width: calc(100%-40px);
}
</style>
<div class="panel">Content</div>
The calc() function requires spaces around + and - operators.
Correct: properly spaced calc() expression
<style>
.panel {
width: calc(100% - 40px);
}
</style>
<div class="panel">Content</div>
Incorrect: accidental semicolon or extra text
<style>
.card {
width: 50% important;
}
</style>
<div class="card">Content</div>
If you intended to use !important, the ! is required.
Correct: proper !important syntax
<style>
.card {
width: 50% !important;
}
</style>
<div class="card">Content</div>
Validate at scale.
Ship accessible websites, faster.
Automated HTML & accessibility validation for large sites. Check thousands of pages against WCAG guidelines and W3C standards in minutes, not days.
Pro Trial
Full Pro access. Cancel anytime.
Start Pro Trial →Join teams across 40+ countries