HTML Guides
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The CSS filter property accepts a specific set of filter functions defined in the CSS Filter Effects Module. When the validator encounters a value it doesn't recognize — such as the legacy IE progid:DXImageTransform.Microsoft. syntax, a made-up function name, or an incorrectly formatted value — it flags it as invalid.
The standard filter functions are: blur(), brightness(), contrast(), drop-shadow(), grayscale(), hue-rotate(), invert(), opacity(), saturate(), sepia(), and url() (for referencing SVG filters). The property also accepts the keyword none. Any value outside this set will trigger the validation error.
Why this matters
Using non-standard filter values creates several problems:
- Browser compatibility: Legacy or proprietary filter syntax (such as Microsoft's
filter: alpha(opacity=50)orfilter: FlipH) only works in old versions of Internet Explorer and is ignored by all modern browsers. - Standards compliance: Invalid CSS can cause parsers to discard the entire declaration or even the surrounding rule block, potentially breaking other styles.
- Future-proofing: Non-standard values may stop working entirely as browsers drop legacy support, leaving your design broken with no fallback.
How to fix it
- Identify the invalid value in the error message.
- Determine what visual effect you're trying to achieve.
- Replace the invalid value with the corresponding standard CSS
filterfunction. - If you're using legacy IE opacity filters, switch to the standard
opacityproperty instead.
Examples
❌ Invalid: Legacy Microsoft filter syntax
<style>
.overlay{
filter:alpha(opacity=50);
}
</style>
✅ Fixed: Use standard opacity property
<style>
.overlay{
opacity:0.5;
}
</style>
❌ Invalid: Misspelled or non-existent filter function
<style>
.photo{
filter:gray();
}
</style>
✅ Fixed: Use the correct grayscale() function
<style>
.photo{
filter:grayscale(100%);
}
</style>
❌ Invalid: Vendor-prefixed or proprietary value
<style>
.card{
filter: progid:DXImageTransform.Microsoft.Blur(pixelradius=5);
}
</style>
✅ Fixed: Use the standard blur() function
<style>
.card{
filter:blur(5px);
}
</style>
❌ Invalid: Bare value without a function
<style>
.image{
filter:50%;
}
</style>
✅ Fixed: Wrap the value in the appropriate function
<style>
.image{
filter:brightness(50%);
}
</style>
Combining multiple filters
You can chain multiple standard filter functions in a single declaration by separating them with spaces:
<style>
.hero-image{
filter:contrast(120%)brightness(90%)blur(1px);
}
</style>
If none of the built-in filter functions achieve the effect you need, you can reference a custom SVG filter using the url() function, which is also a valid filter value:
<svgxmlns="http://www.w3.org/2000/svg"class="visually-hidden">
<filterid="custom-effect">
<feColorMatrixtype="matrix"values="0.3 0.3 0.3 0 0 0.3 0.3 0.3 0 0 0.3 0.3 0.3 0 0 0 0 0 1 0"/>
0.3 0.3 0.3 0 0
0.3 0.3 0.3 0 0
0 0 0 1 0
</filter>
</svg>
<style>
.filtered{
filter: url(#custom-effect);
}
</style>
Negative values for the CSS border property (specifically border-width) are not valid and will be rejected by browsers.
The border-width property only accepts non-negative length values (like 1px, 0.5em) or keyword values (thin, medium, thick). A negative value such as -1px doesn't make sense for a border since you can't have a border with negative thickness.
This error often appears when using inline styles or embedded <style> blocks. It can result from a typo, a calculation error, or a misunderstanding of how border works.
If you're trying to remove a border, use border: none or border-width: 0 instead of a negative value.
Incorrect Example
<divstyle="border:-1px solid black;">
This has an invalid negative border width.
</div>
Correct Example
<!-- Using a valid positive border width -->
<divstyle="border:1px solid black;">
This has a valid border.
</div>
<!-- Removing the border entirely -->
<divstyle="border: none;">
This has no border.
</div>
CSS math functions like calc(), min(), max(), and clamp() follow strict rules about how operands and operators interact. The error "one operand must be a number" most commonly fires in two scenarios: either an operand is missing entirely (e.g., calc(100% - )), or both operands in a multiplication or division carry units (e.g., calc(10px * 5px)). The CSS Values and Units specification requires that for * (multiplication), at least one side must be a unitless <number>. For / (division), the right-hand side must always be a unitless <number>. You cannot multiply two lengths together or divide a length by another length within calc().
This matters for several reasons. Browsers will discard the entire property declaration if the calc() expression is invalid, which can cause layout breakage or fallback to unexpected default values. The W3C validator catches these errors in inline style attributes and embedded <style> blocks, helping you identify expressions that will silently fail in production. Fixing these issues ensures predictable rendering across all browsers.
How the rules work
- Addition and subtraction (
+,-): Both operands must have compatible types (e.g., both lengths, or both percentages, or a mix of length and percentage). Both must be present. - Multiplication (
*): At least one operand must be a plain<number>(unitless). You can writecalc(10px * 3)orcalc(3 * 10px), but notcalc(10px * 5px). - Division (
/): The right-hand operand must be a plain<number>(unitless and non-zero). You can writecalc(100px / 2), but notcalc(100px / 2px).
Examples
Missing operand
A common mistake is leaving out a value on one side of an operator:
<!-- ❌ Wrong: missing operand after the minus sign -->
<divstyle="width:calc(100%-);"></div>
<!-- ✅ Fixed: both operands are present -->
<divstyle="width:calc(100%-50px);"></div>
Multiplying two values with units
You cannot multiply two unit-bearing values together, because the result would be a meaningless type like "px²":
<!-- ❌ Wrong: both operands have units -->
<divstyle="width:calc(10px*5px);"></div>
<!-- ✅ Fixed: one operand is a unitless number -->
<divstyle="width:calc(10px*5);"></div>
Dividing by a value with units
The divisor in a / operation must be a unitless number:
<!-- ❌ Wrong: dividing by a value with units -->
<divstyle="height:calc(500px/2em);"></div>
<!-- ✅ Fixed: divisor is a unitless number -->
<divstyle="height:calc(500px/2);"></div>
Nested calc() with a missing value
Errors can hide inside nested expressions:
<!-- ❌ Wrong: inner calc has an incomplete expression -->
<pstyle="margin-top:calc(2rem+calc(100%*));"></p>
<!-- ✅ Fixed: all operands are present and valid -->
<pstyle="margin-top:calc(2rem+calc(100%*0.5));"></p>
Using variables or keywords where a number is expected
Sometimes a typo or misunderstanding leads to a non-numeric token where a number is required:
<!-- ❌ Wrong: "auto" is not a valid operand in calc() -->
<divstyle="width:calc(auto *2);"></div>
<!-- ✅ Fixed: use a numeric value or percentage -->
<divstyle="width:calc(100%*2);"></div>
To resolve this error, review every calc(), min(), max(), and clamp() expression in your inline styles and stylesheets. Confirm that all operators have valid operands on both sides, that * always has at least one unitless number, and that / always has a unitless number on the right. If you're building expressions dynamically (e.g., via JavaScript or a templating engine), double-check that variables are being interpolated correctly and not producing empty or invalid values.
Character references are how HTML represents special characters that would otherwise be interpreted as markup or that aren't easily typed on a keyboard. They come in three forms:
- Named references like
&,<,© - Decimal numeric references like
<,© - Hexadecimal numeric references like
<,©
All three forms share the same structure: they begin with & and must end with ;. When you omit the trailing semicolon, the HTML parser enters error recovery mode. Depending on the context, it may still resolve the reference (browsers are lenient), but this behavior is not guaranteed and varies across situations. For example, © without a semicolon might still render as ©, but ¬it could be misinterpreted as the ¬ (¬) reference followed by it, producing unexpected output like "¬it" instead of the literal text "¬it".
Why this matters
- Unpredictable rendering: Without the semicolon, browsers use heuristic error recovery that can produce different results depending on surrounding text. What looks fine today might break with different adjacent characters.
- Standards compliance: The WHATWG HTML specification requires the semicolon terminator. Omitting it is a parse error.
- Maintainability: Other developers (or future you) may not realize the ampersand was intended as a character reference, making the code harder to read and maintain.
- Data integrity: In URLs within
hrefattributes, a missing semicolon on a character reference can corrupt query parameters and produce broken links.
How to fix it
- Add the missing semicolon to the end of every character reference.
- If you meant a literal ampersand, use
&instead of a bare&. This is especially common in URLs with query strings. - Search your document for patterns like
&somethingwithout a trailing;to catch all instances.
Examples
❌ Missing semicolon on named references
<p>5 < 10 and 10 > 5</p>
<p>© 2024 All rights reserved</p>
✅ Properly terminated named references
<p>5 < 10 and 10 > 5</p>
<p>© 2024 All rights reserved</p>
❌ Missing semicolon on numeric references
<p>The letter A: A</p>
<p>Hex example: A</p>
✅ Properly terminated numeric references
<p>The letter A: A</p>
<p>Hex example: A</p>
❌ Bare ampersand in a URL (common mistake)
<ahref="https://example.com/search?name=alice&age=30">Search</a>
Here the validator sees &age and tries to interpret it as a character reference without a semicolon.
✅ Escaped ampersand in a URL
<ahref="https://example.com/search?name=alice&age=30">Search</a>
❌ Ambiguous reference causing wrong output
<p>The entity ¬it; doesn't exist, but ¬ without a semicolon resolves to ¬</p>
✅ Use & when you want a literal ampersand
<p>The text &notit is displayed literally when properly escaped.</p>
A quick rule of thumb: every & in your HTML should either be the start of a complete, semicolon-terminated character reference, or it should itself be written as &.
The border shorthand property accepts up to three values: a width (e.g., 1px), a style (e.g., solid), and a color (e.g., black). When the validator encounters "undefined" in the color position, it rightfully rejects it because undefined is not a recognized CSS color keyword, hex value, or color function.
This issue most commonly appears in projects that use JavaScript to dynamically set inline styles. When a variable intended to hold a color value is undefined—perhaps because it wasn't initialized, a configuration value is missing, or a function didn't return a result—the rendered HTML ends up with a literal style="border: 1px solid undefined" in the markup. Build tools, templating engines, or server-side rendering can also produce this output if a variable isn't properly resolved.
Beyond failing validation, this is a real problem because browsers will discard the entire border declaration when they encounter an invalid value. This means the border won't render at all, which may break your layout or visual design in ways that are hard to debug. Ensuring valid CSS values keeps your styling predictable across all browsers.
How to fix it
- Check for dynamic values. If the color is set via JavaScript or a templating engine, ensure the variable always resolves to a valid color string. Add fallback values or default assignments.
- Replace the invalid value. Substitute
undefinedwith a proper CSS color — a named color (red,black), a hex code (#333), anrgb()orhsl()function, or eventransparentorcurrentcolor. - Remove the declaration. If no border is needed, remove the
borderproperty entirely rather than leaving an invalid value in place.
Examples
Incorrect: literal undefined as a color value
<divstyle="border:1px solid undefined;">Content</div>
The validator rejects undefined because it is not a valid CSS color.
Incorrect: JavaScript producing undefined in markup
<script>
constborderColor=undefined;// missing configuration
document.getElementById("box").style.border="2px dashed "+borderColor;
</script>
This produces border: 2px dashed undefined on the element.
Correct: using a valid color value
<divstyle="border:1px solid black;">Content</div>
Correct: using a hex code or rgb() function
<divstyle="border:2px dashed #ff6600;">Content</div>
<divstyle="border:3px dotted rgb(0,128,255);">Content</div>
Correct: providing a fallback in JavaScript
<divid="box">Content</div>
<script>
constborderColor=getUserColor()||"#333";
document.getElementById("box").style.border="2px solid "+borderColor;
</script>
By using || "#333", you ensure a valid color is always applied even when getUserColor() returns undefined.
Correct: using separate border properties
If you prefer more granular control, you can define each border sub-property individually:
<divstyle="border-width:1px;border-style: solid;border-color: black;">Content</div>
Valid border shorthand reference
The border shorthand follows this pattern:
selector{
border: <width> <style> <color>;
}
- Width:
1px,2px,thin,medium,thick - Style:
solid,dashed,dotted,double,groove,ridge,inset,outset,none - Color: any valid CSS color — named colors (
red,blue), hex (#000),rgb(),hsl(),currentcolor, ortransparent
All three values are optional in the shorthand, but any value you do include must be valid. The string undefined is never a valid CSS value. If your styles are generated dynamically, always validate or sanitize the output before it reaches the HTML.
The color property, along with properties like background-color, border-color, and outline-color, expects values that conform to the CSS Color specification. The validator triggers this error when it encounters something that doesn't match any valid color syntax. Common causes include:
- Plain numbers like
0or123— numbers alone aren't colors. - Typos in color keywords such as
greaninstead ofgreen, ortrasparentinstead oftransparent. - Malformed hex values like
#GGG(invalid hex characters) or#12345(wrong number of digits — hex colors must be 3, 4, 6, or 8 digits). - Incorrect function syntax such as
rgb(255 255 255 / 50)missing the%on the alpha value, or using legacy commas mixed with modern space-separated syntax. - Missing units or hash symbols like
000000instead of#000000.
This matters because browsers handle invalid color values unpredictably. Most will simply ignore the declaration entirely, which means the element inherits its color from a parent or falls back to the browser default — potentially making text unreadable against its background. Writing valid CSS ensures consistent rendering across all browsers and improves the maintainability of your code.
Valid CSS color formats
CSS supports several color formats:
| Format | Example | Notes |
|---|---|---|
| Named colors | red, blue, transparent | 148 predefined keywords |
| Hexadecimal | #ff0000, #f00 | 3, 4, 6, or 8 digits |
rgb() / rgba() | rgb(255, 0, 0) | Comma or space-separated |
hsl() / hsla() | hsl(0, 100%, 50%) | Hue, saturation, lightness |
currentcolor | currentcolor | Inherits the current color value |
Examples
Invalid: plain number as a color
A bare number is not a recognized color value:
<style>
.example{
color:0;
}
</style>
Invalid: typo in a color keyword
<style>
.example{
background-color: trasparent;
}
</style>
Invalid: hex value missing the # prefix
<style>
.example{
color:000000;
}
</style>
Invalid: hex value with wrong digit count
<style>
.example{
color:#12345;
}
</style>
Fixed: using a named color keyword
<style>
.example{
color: black;
}
</style>
Fixed: using a hexadecimal color
<style>
.example{
color:#000000;
}
</style>
Fixed: using rgb()
<style>
.example{
color:rgb(0,0,0);
}
</style>
Fixed: using hsl()
<style>
.example{
color:hsl(0,0%,0%);
}
</style>
Fixed: using rgba() for semi-transparent color
<style>
.example{
color:rgba(0,0,0,0.5);
}
</style>
Fixed: correcting the transparent keyword typo
<style>
.example{
background-color: transparent;
}
</style>
If you're unsure whether a value is valid, browser DevTools can help — most browsers will strike through or ignore invalid property values in the Styles panel, giving you a quick visual indicator of the problem.
The CSS display property controls how an element generates boxes in the layout. It determines whether an element behaves as a block-level or inline-level element and defines the layout model for its children (e.g., flow, flexbox, or grid). Because display is fundamental to page layout, using an invalid value means the browser will ignore the declaration entirely, potentially causing unexpected rendering.
Common causes of this error include:
- Typos — writing
dipslay: block,display: blok, ordisplay: flxinstead of the correct keywords. - Confusing values from other properties — using values like
center,hidden,absolute, orrelative, which belong to properties liketext-align,visibility, orposition, notdisplay. - Invented or outdated values — using non-standard or deprecated values that browsers don't recognize, such as
display: box(an old prefixed flexbox syntax without the prefix). - Missing vendor prefixes — some older syntaxes like
-webkit-flexwere valid in certain browsers but are not standard CSS values.
The valid values for display include: block, inline, inline-block, flex, inline-flex, grid, inline-grid, flow-root, none, contents, table, table-row, table-cell, table-caption, table-column, table-column-group, table-footer-group, table-header-group, table-row-group, list-item, and the multi-keyword syntax like block flow, block flex, or inline grid.
Using invalid CSS values is a problem because browsers silently discard declarations they don't understand. This means your intended layout won't be applied, and debugging can be difficult since no visible error appears in the browser. Validating your CSS catches these mistakes early.
Examples
Invalid: typo in the display value
<divstyle="display: flx;">
<p>This container was meant to be a flex container.</p>
</div>
Fixed: correct flex value
<divstyle="display: flex;">
<p>This container is now a flex container.</p>
</div>
Invalid: using a value from another property
<navstyle="display: center;">
<ahref="/">Home</a>
</nav>
The value center does not belong to display. If the goal is to center content, use a valid display value combined with appropriate alignment properties.
Fixed: using flex with centering
<navstyle="display: flex;justify-content: center;">
<ahref="/">Home</a>
</nav>
Invalid: using a position value instead of a display value
<divstyle="display: absolute;">
<p>Overlay content</p>
</div>
Fixed: using the correct property
<divstyle="position: absolute;display: block;">
<p>Overlay content</p>
</div>
Invalid: using a non-standard value
<ulstyle="display: box;">
<li>Item 1</li>
<li>Item 2</li>
</ul>
Fixed: using the standard flexbox value
<ulstyle="display: flex;">
<li>Item 1</li>
<li>Item 2</li>
</ul>
The left property specifies the horizontal offset of a positioned element — one that has its position set to relative, absolute, fixed, or sticky. The W3C validator checks CSS within style attributes and <style> elements, and it will flag any value it cannot recognize as a valid left value.
Common causes of this error include:
- Misspelled or non-existent units: Writing
10 px(with a space),10pixels, or20ppxinstead of10px. - Unsupported keywords: Using values like
none,center, ormiddle, which are not valid for theleftproperty. - Missing units on non-zero numbers: Writing
left: 10instead ofleft: 10px. Zero is the only number that doesn't require a unit. - Typos in keyword values: Writing
auтоorautooinstead ofauto. - CSS custom properties in inline styles: Using
var(--offset)in astyleattribute may trigger validation warnings depending on the validator's CSS level.
The valid values for the left property are:
<length>: A numeric value with a unit, such as10px,2em,3rem,1vw.<percentage>: A percentage relative to the containing block's width, such as50%.auto: Lets the browser determine the position (this is the default).- Global keywords:
inherit,initial,unset, andrevert.
Using an invalid value means the browser will ignore the declaration entirely, which can break your layout. Fixing these values ensures consistent rendering across browsers and compliance with CSS standards.
Examples
Invalid: Using an unsupported keyword
The keyword none is not a valid value for the left property.
<divstyle="position: absolute;left: none;">Positioned element</div>
Invalid: Missing unit on a non-zero number
A bare number (other than 0) is not valid without a CSS unit.
<divstyle="position: relative;left:20;">Shifted element</div>
Invalid: Misspelled unit
The unit xp does not exist in CSS.
<divstyle="position: absolute;left:15xp;">Positioned element</div>
Valid: Using a length value
<divstyle="position: absolute;left:20px;">20 pixels from the left</div>
Valid: Using a percentage
<divstyle="position: absolute;left:50%;">Offset by 50% of containing block</div>
Valid: Using the auto keyword
<divstyle="position: absolute;left: auto;">Browser-determined position</div>
Valid: Using zero without a unit
Zero does not require a unit in CSS.
<divstyle="position: absolute;left:0;">Flush with the left edge</div>
Valid: Using inherit
<divstyle="position: relative;left: inherit;">Inherits left value from parent</div>
To fix this error, identify the invalid value the validator is reporting and replace it with one of the accepted value types listed above. If you intended to reset the position, use auto or 0. If you meant to remove a previously set left value, use initial or unset rather than an unsupported keyword like none.
The mask CSS shorthand property allows you to partially or fully hide portions of an element by applying a graphical mask. It is a shorthand for several sub-properties including mask-image, mask-mode, mask-repeat, mask-position, mask-clip, mask-origin, mask-size, and mask-composite. Because it's a shorthand, each value you provide must correspond to one of these sub-properties' accepted values. The validator triggers this error when it encounters a value that doesn't fit any of them — for example, an arbitrary keyword, a misspelled function name, or an unsupported syntax.
Common causes of this error include:
- Arbitrary keywords — Using made-up names like
star-shapeorcircle-maskthat aren't valid CSS values. - Misspelled functions or keywords — Typos such as
lnear-gradient()instead oflinear-gradient(), ornoeninstead ofnone. - Browser-prefixed values without the standard value — Using
-webkit-masksyntax or values that don't align with the standardmaskproperty. - Invalid shorthand combinations — Providing sub-property values in an order or combination the shorthand doesn't accept.
- Missing
url()wrapper — Referencing an image file path directly without wrapping it in theurl()function.
This matters for standards compliance because browsers may silently ignore invalid mask values, resulting in the mask not being applied at all. Your design could look completely different than intended, and the failure may be hard to debug without validation.
Valid mask values
The mask property accepts one or more comma-separated mask layers. Each layer can include:
none— No mask is applied.url()— A reference to an SVG mask element or an image file (e.g.,url(mask.svg),url(mask.png)).- CSS image functions — Such as
linear-gradient(),radial-gradient(),conic-gradient(),image(), etc. - Geometry box keywords (for
mask-clip/mask-origin) — Such ascontent-box,padding-box,border-box,fill-box,stroke-box,view-box. - Compositing keywords (for
mask-composite) — Such asadd,subtract,intersect,exclude.
Examples
Incorrect: arbitrary keyword as a mask value
<divstyle="mask: star-shape;">
Masked Content
</div>
The value star-shape is not a recognized mask value and will be rejected by the validator.
Incorrect: missing url() function
<divstyle="mask: star.svg;">
Masked Content
</div>
A bare file path is not valid. Image references must be wrapped in the url() function.
Correct: using url() to reference a mask image
<divstyle="mask:url(star.svg);">
Masked Content
</div>
Correct: using none to explicitly disable masking
<divstyle="mask: none;">
No Mask Applied
</div>
Correct: using a gradient as a mask
<divstyle="mask:linear-gradient(to right, transparent, black);">
Fading Content
</div>
Correct: combining multiple shorthand values
<divstyle="mask:url(mask.png) no-repeat center / contain;">
Masked Content
</div>
This sets the mask image, repeat behavior, position, and size in a single shorthand declaration.
Correct: multiple mask layers
<divstyle="mask:url(shape.svg) no-repeat,linear-gradient(to bottom, black, transparent);">
Multi-layer Mask
</div>
When fixing this error, double-check your value against the CSS Masking specification on MDN. If you're using vendor-prefixed versions like -webkit-mask, also ensure the standard mask property is present with valid values for forward compatibility.
The id attribute uniquely identifies an element within a document. According to the WHATWG HTML living standard, if the id attribute is specified, its value must be non-empty and must not contain any ASCII whitespace characters. The attribute itself is optional — you don't need to include it — but when you do, it must have a valid value. Setting id="" violates this rule because the empty string is not a valid identifier.
This issue commonly occurs when code is generated dynamically (e.g., by a templating engine or JavaScript framework) and the variable intended to populate the id value resolves to an empty string. It can also happen when developers add the attribute as a placeholder and forget to fill it in.
Why this matters
- Standards compliance: An empty
idviolates the HTML specification, making your document invalid. - Accessibility: Assistive technologies like screen readers rely on
idattributes to associate<label>elements with form controls. An emptyidbreaks this association, making forms harder to use for people who depend on these tools. - JavaScript and CSS: Methods like
document.getElementById("")and selectors like#(with no identifier) will not work as expected. An emptyidcan cause subtle, hard-to-debug issues in your scripts and styles. - Browser behavior: While browsers are generally forgiving, an empty
idleads to undefined behavior. Different browsers may handle it inconsistently.
How to fix it
- Assign a meaningful value: Give the
ida descriptive, unique value that identifies the element's purpose (e.g.,id="country-select"). - Remove the attribute: If you don't need the
id, simply remove it from the element altogether. - Fix dynamic generation: If a templating engine or framework is producing the empty value, add a conditional check to either output a valid
idor omit the attribute entirely.
Examples
❌ Incorrect: empty id attribute
<labelfor="country">Country</label>
<selectid=""name="country">
<optionvalue="us">United States</option>
<optionvalue="ca">Canada</option>
</select>
This triggers the validation error because id="" is an empty string.
✅ Correct: meaningful id value
<labelfor="country">Country</label>
<selectid="country"name="country">
<optionvalue="us">United States</option>
<optionvalue="ca">Canada</option>
</select>
The id now has a valid, non-empty value, and the <label> element's for attribute correctly references it.
✅ Correct: id attribute removed entirely
<label>
Country
<selectname="country">
<optionvalue="us">United States</option>
<optionvalue="ca">Canada</option>
</select>
</label>
If you don't need the id, remove it. Here, the <label> wraps the <select> directly, so the for/id association isn't needed — the implicit label works just as well.
The aria-activedescendant attribute tells assistive technologies which child element within a composite widget — such as a combobox, listbox, or autocomplete dropdown — is currently "active" or focused. Instead of moving actual DOM focus to each option, the parent element (like an input) retains focus while aria-activedescendant points to the visually highlighted option by referencing its id. This allows screen readers to announce the active option without disrupting keyboard interaction on the input.
When aria-activedescendant is set to an empty string (""), it creates an invalid state. The HTML and ARIA specifications require that any ID reference attribute either contains a valid, non-empty ID token or is omitted altogether. An empty string is not a valid ID, so the W3C validator flags this as an error: Bad value "" for attribute "aria-activedescendant" on element "input": An ID must not be the empty string.
This problem commonly occurs in JavaScript-driven widgets where aria-activedescendant is cleared by setting it to "" when no option is highlighted — for example, when a dropdown closes or the user clears their selection. While the developer's intent is correct (indicating that nothing is active), the implementation is wrong.
Why this matters
- Accessibility: Screen readers may behave unpredictably when encountering an empty ID reference. Some may silently ignore it, while others may announce errors or fail to convey widget state correctly.
- Standards compliance: The ARIA specification explicitly requires ID reference values to be non-empty strings that match an existing element's
id. - Browser consistency: Browsers handle invalid ARIA attributes inconsistently, which can lead to different experiences across platforms and assistive technologies.
How to fix it
- Remove the attribute when no descendant is active. Use
removeAttribute('aria-activedescendant')in JavaScript instead of setting it to an empty string. - Set a valid ID when a descendant becomes active, pointing to the
idof the currently highlighted or selected option. - Never render the attribute in HTML with an empty value. If your framework or templating engine conditionally renders attributes, ensure it omits the attribute entirely rather than outputting
aria-activedescendant="".
Examples
Incorrect: empty string value
This triggers the W3C validation error because the attribute value is an empty string.
<inputtype="text"role="combobox"aria-activedescendant=""/>
Correct: attribute omitted when no option is active
When nothing is active, simply leave the attribute off.
<inputtype="text"role="combobox"aria-expanded="false"/>
Correct: valid ID reference when an option is active
When a user highlights an option, set aria-activedescendant to that option's id.
<divrole="combobox">
<input
type="text"
role="combobox"
aria-expanded="true"
aria-controls="suggestions"
aria-activedescendant="option2"/>
<ulid="suggestions"role="listbox">
<liid="option1"role="option">Apple</li>
<liid="option2"role="option"aria-selected="true">Banana</li>
<liid="option3"role="option">Cherry</li>
</ul>
</div>
Correct: managing the attribute dynamically with JavaScript
The key fix in JavaScript is using removeAttribute instead of setting the value to an empty string.
<divrole="combobox">
<input
id="search"
type="text"
role="combobox"
aria-expanded="true"
aria-controls="results"/>
<ulid="results"role="listbox">
<liid="result1"role="option">First result</li>
<liid="result2"role="option">Second result</li>
</ul>
</div>
<script>
constinput=document.getElementById('search');
functionsetActiveOption(optionId){
if(optionId){
input.setAttribute('aria-activedescendant',optionId);
}else{
// Remove the attribute instead of setting it to ""
input.removeAttribute('aria-activedescendant');
}
}
</script>
In summary, always ensure aria-activedescendant either points to a real, non-empty id or is removed from the element. Never set it to an empty string.
The <textarea> element represents a multi-line plain-text editing control, commonly used for comments, feedback forms, and other free-form text input. The rows attribute specifies the number of visible text lines the textarea should display. According to the HTML specification, when the rows attribute is present, its value must be a valid positive integer — meaning a string of one or more digits representing a number greater than zero (e.g., 1, 5, 20). An empty string ("") does not meet this requirement.
This issue typically occurs when a template engine, CMS, or JavaScript framework dynamically sets the rows attribute but outputs an empty value instead of a number, or when a developer adds the attribute as a placeholder intending to fill it in later.
Why this matters
- Standards compliance: The HTML specification explicitly requires
rowsto be a positive integer when present. An empty string violates this rule. - Unpredictable rendering: Browsers fall back to a default value (typically
2) when they encounter an invalidrowsvalue, but this behavior isn't guaranteed to be consistent across all browsers and versions. - Maintainability: Invalid attributes can mask bugs in dynamic code that was supposed to provide a real value.
How to fix it
You have two options:
- Set a valid positive integer: Replace the empty string with a number like
4,5, or whatever suits your design. - Remove the attribute: If you don't need to control the number of visible rows (or prefer to handle sizing with CSS), simply omit the
rowsattribute. The browser will use its default.
If the value is generated dynamically, ensure your code has a fallback so it never outputs an empty string. You can also control the textarea's height using CSS (height or min-height) instead of relying on the rows attribute.
Examples
❌ Invalid: empty string for rows
<textareaname="comments"rows=""cols="25">
</textarea>
This triggers the error because "" is not a valid positive integer.
✅ Fixed: providing a valid positive integer
<textareaname="comments"rows="5"cols="25">
</textarea>
Setting rows="5" tells the browser to display five visible lines of text.
✅ Fixed: removing the attribute entirely
<textareaname="comments"cols="25">
</textarea>
When rows is omitted, the browser uses its default (typically 2 rows). This is perfectly valid.
✅ Alternative: using CSS for sizing
<textareaname="comments"style="height:10em;width:25ch;">
</textarea>
If you need precise control over the textarea's dimensions, CSS properties like height, min-height, and width give you more flexibility than the rows and cols attributes. In this case, you can safely leave both attributes off.
The HTML living standard defines that scripts with type="module" are always fetched in parallel and evaluated after the document has been parsed, which is the same behavior that the defer attribute provides for classic scripts. Because this deferred execution is an inherent characteristic of module scripts, the spec explicitly forbids combining the two. Including both doesn't change how the browser handles the script, but it signals a misunderstanding of how modules work and produces invalid HTML.
This validation error commonly arises when developers migrate classic scripts to ES modules. A classic script like <script defer src="app.js"></script> relies on the defer attribute to avoid blocking the parser. When converting to a module by adding type="module", it's natural to leave defer in place — but it's no longer needed or allowed.
It's worth noting that the async attribute is valid on module scripts and does change their behavior. While defer is redundant because modules are already deferred, async overrides that default and causes the module to execute as soon as it and its dependencies have finished loading, rather than waiting for HTML parsing to complete.
How to Fix
Remove the defer attribute from any <script> element that has type="module". No other changes are needed — the loading and execution behavior will remain identical.
If you intentionally want the script to run as soon as possible (before parsing completes), use async instead of defer. But if you want the standard deferred behavior, simply omit both attributes and let the module default take effect.
Examples
❌ Incorrect: defer combined with type="module"
<scripttype="module"defersrc="app.js"></script>
The defer attribute is redundant here and causes a validation error.
✅ Correct: module script without defer
<scripttype="module"src="app.js"></script>
Module scripts are deferred automatically, so this behaves exactly the same as the incorrect example above but is valid HTML.
✅ Correct: using async with a module (when needed)
<scripttype="module"asyncsrc="analytics.js"></script>
Unlike defer, the async attribute is permitted on module scripts. It causes the module to execute as soon as it's ready, without waiting for HTML parsing to finish.
✅ Correct: classic script with defer
<scriptdefersrc="app.js"></script>
For classic (non-module) scripts, the defer attribute is valid and necessary if you want deferred execution.
Hidden inputs are designed to carry data between the client and server without any user interaction or visual presence. The browser does not render them, screen readers do not announce them, and they are entirely excluded from the accessibility tree. Because aria-* attributes exist solely to convey information to assistive technologies, adding them to an element that assistive technologies cannot perceive is contradictory and meaningless.
The HTML specification explicitly prohibits aria-* attributes on input elements with type="hidden". This restriction exists because WAI-ARIA attributes — such as aria-label, aria-invalid, aria-describedby, aria-required, and all others in the aria-* family — are meant to enhance the accessible representation of interactive or visible elements. A hidden input has no such representation, so these attributes have nowhere to apply.
This issue commonly arises when:
- JavaScript frameworks or templating engines apply
aria-*attributes indiscriminately to all form inputs, regardless of type. - A developer changes an input's type from
"text"to"hidden"but forgets to remove the accessibility attributes that were relevant for the visible version. - Form libraries or validation plugins automatically inject attributes like
aria-invalidonto every input in a form.
To fix the issue, simply remove all aria-* attributes from any input element that has type="hidden". If the aria-* attribute was meaningful on a previously visible input, no replacement is needed — the hidden input doesn't participate in the user experience at all.
Examples
Incorrect: hidden input with aria-invalid
<formaction="/submit"method="post">
<inputtype="hidden"name="referer"value="https://example.com"aria-invalid="false">
<buttontype="submit">Submit</button>
</form>
Correct: hidden input without aria-* attributes
<formaction="/submit"method="post">
<inputtype="hidden"name="referer"value="https://example.com">
<buttontype="submit">Submit</button>
</form>
Incorrect: hidden input with multiple aria-* attributes
<formaction="/save"method="post">
<input
type="hidden"
name="session_token"
value="abc123"
aria-label="Session token"
aria-required="true"
aria-describedby="token-help">
<buttontype="submit">Save</button>
</form>
Correct: all aria-* attributes removed
<formaction="/save"method="post">
<inputtype="hidden"name="session_token"value="abc123">
<buttontype="submit">Save</button>
</form>
Correct: aria-* attributes on a visible input (where they belong)
If the input is meant to be visible and accessible, use an appropriate type value instead of "hidden":
<formaction="/login"method="post">
<labelfor="username">Username</label>
<input
type="text"
id="username"
name="username"
aria-required="true"
aria-invalid="false"
aria-describedby="username-help">
<pid="username-help">Enter your registered email or username.</p>
<buttontype="submit">Log in</button>
</form>
According to the HTML specification, the content model for <ul> is strictly limited to zero or more <li> elements. Any text node placed directly inside the <ul> violates this rule, even if it seems harmless or invisible. Browsers may still render the page, but the resulting DOM structure is technically invalid and can lead to unpredictable behavior across different browsers and assistive technologies.
This matters for accessibility because screen readers rely on proper list structure to announce the number of items and allow users to navigate between them. Stray text nodes inside a <ul> can confuse these tools, causing list items to be miscounted or the text to be read in an unexpected context.
There are several common scenarios that trigger this error:
Loose text used as a list title. Developers sometimes place a heading or label directly inside the <ul> to describe the list. This text must be moved outside the list element.
Stray or other entities between list items. This often happens in templating systems or when code is concatenated, where characters or other text nodes end up between <li> elements. These should be removed entirely, since spacing between list items should be controlled with CSS.
Accidentally placing inline content without wrapping it in <li>. Sometimes content that should be a list item is simply missing its <li> wrapper.
Examples
❌ Text used as a list title inside <ul>
<ul>
Fruits
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
</ul>
The word "Fruits" is a text node directly inside the <ul>, which is not allowed.
✅ Move the title outside the list
<h3>Fruits</h3>
<ul>
<li>Apple</li>
<li>Orange</li>
<li>Banana</li>
</ul>
Using a heading before the list is semantically clear. You can also use a <p> or <span> if a heading isn't appropriate.
❌ entities between list items
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
Each is a text node sitting directly inside the <ul>, triggering the error.
✅ Remove the entities and use CSS for spacing
<ul>
<li>First item</li>
<li>Second item</li>
<li>Third item</li>
</ul>
ulli{
margin-bottom:0.5em;
}
Any visual spacing between list items should be handled with CSS margin or padding, not with HTML entities.
❌ Unwrapped content that should be a list item
<ul>
<li>Milk</li>
Eggs
<li>Bread</li>
</ul>
✅ Wrap the content in an <li> element
<ul>
<li>Milk</li>
<li>Eggs</li>
<li>Bread</li>
</ul>
The same rules apply to <ol> (ordered lists) and <menu> elements — their direct children must be <li> elements, and text nodes are not permitted. If your list is generated dynamically by a templating engine or JavaScript, check the output carefully for stray whitespace or text that may have been injected between list items.
The CSS fill property does not accept linear-gradient() as a valid value.
The fill property is used primarily with SVG elements to define the color that fills the interior of a shape. It accepts simple color values like named colors, hex codes, rgb(), hsl(), currentColor, none, or a reference to an SVG paint server using url().
CSS gradients such as linear-gradient() are classified as <image> values, not <color> values, so they cannot be used directly with fill. This is different from properties like background or background-image, which do accept gradient values.
If you need a gradient fill on an SVG element, you should define a <linearGradient> inside an SVG <defs> block and reference it with url().
How to fix it
Invalid: Using linear-gradient() with fill:
<svgwidth="100"height="100">
<rectwidth="100"height="100"style="fill:linear-gradient(to right, red, blue);"/>
</svg>
Valid: Using an SVG <linearGradient> definition:
<svgwidth="100"height="100">
<defs>
<linearGradientid="myGradient"x1="0"y1="0"x2="1"y2="0">
<stopoffset="0%"stop-color="red"/>
<stopoffset="100%"stop-color="blue"/>
</linearGradient>
</defs>
<rectwidth="100"height="100"fill="url(#myGradient)"/>
</svg>
The x1, y1, x2, and y2 attributes on <linearGradient> control the direction of the gradient. In this example, x1="0" x2="1" creates a left-to-right gradient, equivalent to to right in CSS.
Direct text nodes inside select are not permitted content. Browsers typically ignore or mangle that text, leading to inconsistent rendering and confusing experiences for screen reader users. It also breaks conformance, which can impact maintainability and automated tooling. The right approach is to keep instructional text in a corresponding label, or provide a non-selectable prompt using a disabled, selected option. Group labels must be provided with optgroup elements, not free text.
To fix it, remove any raw text inside the select. If you need a prompt, add a first option with value="" and disabled selected hidden for a placeholder-like experience, or rely on a visible label. Ensure all selectable items are wrapped in option, and any grouping uses optgroup with its label attribute. Always associate the select with a label via for/id for accessibility.
Examples
Triggers the error (text node inside select)
<select>
Please select an option:
<optionvalue="1">Option 1</option>
<optionvalue="2">Option 2</option>
</select>
Correct: move instructions to a label
<labelfor="flavor">Please select a flavor:</label>
<selectid="flavor"name="flavor">
<optionvalue="vanilla">Vanilla</option>
<optionvalue="chocolate">Chocolate</option>
</select>
Correct: provide a non-selectable prompt inside select
<labelfor="country">Country</label>
<selectid="country"name="country"required>
<optionvalue=""disabledselectedhidden>Select a country</option>
<optionvalue="us">United States</option>
<optionvalue="ca">Canada</option>
</select>
Correct: use optgroup for grouping, not free text
<labelfor="city">City</label>
<selectid="city"name="city">
<optgrouplabel="USA">
<optionvalue="nyc">New York</option>
<optionvalue="la">Los Angeles</option>
</optgroup>
<optgrouplabel="Canada">
<optionvalue="toronto">Toronto</option>
<optionvalue="vancouver">Vancouver</option>
</optgroup>
</select>
Correct: full document (for context)
<!doctype html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>Select without stray text</title>
</head>
<body>
<form>
<labelfor="size">Choose a size:</label>
<selectid="size"name="size">
<optionvalue=""disabledselectedhidden>Select a size</option>
<optionvalue="s">Small</option>
<optionvalue="m">Medium</option>
<optionvalue="l">Large</option>
</select>
</form>
</body>
</html>
Tips:
- Put instructions in a
labelor surrounding text, not insideselect. - Every choice must be an
option; useoptgroupwithlabelto name groups. - For placeholders, prefer a disabled, selected first
option; avoid raw text nodes.
The background CSS property is a shorthand that can accept values for background-color, background-image, background-position, background-size, background-repeat, background-origin, background-clip, and background-attachment. When the validator encounters an unrecognized value, it tries to match it against individual sub-properties like background-color. If the value doesn't match any of them, you'll see this error.
Common causes include typos in color names (e.g., bleu instead of blue), malformed hex codes (e.g., #gggggg or a missing #), incorrect function syntax (e.g., rgb(255 0 0 with a missing parenthesis), or using values that simply don't exist in CSS. This error can also appear when a CSS custom property (variable) is used in inline styles and the validator can't resolve it, or when a browser-specific value is used that isn't part of the CSS specification.
Fixing this issue ensures your styles render predictably across browsers. While browsers are often forgiving and may ignore invalid declarations silently, relying on that behavior can lead to inconsistent rendering. Standards-compliant CSS is easier to maintain and debug.
How to Fix
- Check for typos in color names, hex codes, or function syntax.
- Verify the value format — hex colors need a
#prefix,rgb()andrgba()need proper comma-separated or space-separated values with closing parentheses. - Use
background-colorinstead of the shorthandbackgroundif you only intend to set a color. This makes your intent clearer and reduces the chance of conflicting shorthand values. - Remove vendor-prefixed or non-standard values that the validator doesn't recognize.
Examples
Incorrect — Typo in color name
<divstyle="background: aquaa;">Content</div>
aquaa is not a valid CSS color name, so the validator rejects it.
Correct — Valid color name
<divstyle="background: aqua;">Content</div>
Incorrect — Malformed hex code
<divstyle="background:#xyz123;">Content</div>
Hex color codes only allow characters 0–9 and a–f.
Correct — Valid hex code
<divstyle="background:#00a123;">Content</div>
Incorrect — Missing hash symbol
<divstyle="background: ff0000;">Content</div>
Without the #, the validator interprets ff0000 as an unknown keyword.
Correct — Hex code with hash
<divstyle="background:#ff0000;">Content</div>
Incorrect — Broken rgb() syntax
<divstyle="background:rgb(255,0,300);">Content</div>
RGB channel values must be between 0 and 255 (or 0% to 100%).
Correct — Valid rgb() value
<divstyle="background:rgb(255,0,128);">Content</div>
Correct — Using background-color for clarity
When you only need to set a color, prefer the specific background-color property over the shorthand:
<divstyle="background-color:rgba(255,0,0,0.5);">Semi-transparent red</div>
Correct — Valid shorthand with image and other properties
<divstyle="background:url('image.jpg') no-repeat center / cover;">Content</div>
Note the / between background-position (center) and background-size (cover) — this is required syntax in the shorthand.
The padding-right property defines the space between an element's content and its right border. According to the CSS Box Model specification, padding represents internal space within an element, and conceptually, negative internal space doesn't make sense — you can't have less than zero space between content and its border. This rule applies equally to all padding properties: padding-top, padding-right, padding-bottom, padding-left, and the padding shorthand.
Browsers will typically ignore or discard a negative padding value, meaning your intended layout adjustment won't take effect. Beyond simply being invalid CSS, this can lead to inconsistent rendering across browsers and unexpected layout behavior. Relying on invalid values makes your stylesheets fragile and harder to maintain.
If your goal is to pull an element closer to its neighbor or create an overlapping effect, margin-right is the appropriate property to use. Unlike padding, margins are explicitly allowed to have negative values. Negative margins reduce the space between elements or even cause them to overlap, which is often the actual intent behind a negative padding attempt.
How to Fix
- Set the value to
0or a positive number. If you simply want no padding, use0. If you need some spacing, use a positive value. - Use
margin-rightfor negative spacing. If you need to reduce external space or create overlap, switch to a negative margin instead. - Re-evaluate your layout approach. In some cases, using
transform: translateX(), Flexboxgap, or Grid layout may achieve the desired result more cleanly than negative values on any property.
Examples
Incorrect: negative padding value
<style>
.sidebar{
padding-right:-10px;
}
</style>
<divclass="sidebar">
<p>Sidebar content</p>
</div>
This triggers the validator error because -10px is not a valid value for padding-right.
Fixed: using zero or a positive value
<style>
.sidebar{
padding-right:0;
}
</style>
<divclass="sidebar">
<p>Sidebar content</p>
</div>
Fixed: using a negative margin instead
If the intent was to reduce external spacing on the right side, use margin-right:
<style>
.sidebar{
padding-right:0;
margin-right:-10px;
}
</style>
<divclass="sidebar">
<p>Sidebar content</p>
</div>
Fixed: using transform for visual offset
If the goal is to visually shift the element without affecting document flow, transform is another option:
<style>
.sidebar{
padding-right:0;
transform:translateX(10px);
}
</style>
<divclass="sidebar">
<p>Sidebar content</p>
</div>
Quick reference: padding vs. margin
| Property | Negative values allowed? | Purpose |
|---|---|---|
padding-right | No | Space between content and border |
margin-right | Yes | Space between the element's border and surrounding elements |
Choose the property that matches your layout intent, and remember that all four padding directions — padding-top, padding-right, padding-bottom, and padding-left — follow the same non-negative rule.
The <iframe> element is designed to embed another browsing context (essentially a nested document) within the current page. According to the HTML specification, the content model of <iframe> is nothing — it must not contain any text nodes or child elements. Any text placed directly inside the <iframe> tags is invalid HTML.
Historically, some older HTML versions allowed fallback text inside <iframe> for browsers that didn't support iframes. This is no longer the case in modern HTML. All current browsers support <iframe>, so there is no need for fallback content. The W3C validator flags this as an error because text or elements between the <iframe> tags violate the current HTML specification.
There are two valid ways to specify the content an iframe should display:
srcattribute — Provide a URL pointing to the document you want to embed.srcdocattribute — Provide inline HTML content directly as the attribute's value. The HTML is written as a string with appropriate character escaping (e.g.,<for<and"for"if using double-quoted attributes).
If you were using text inside <iframe> as a description or fallback, consider using the title attribute instead to provide an accessible label for the embedded content.
Examples
❌ Invalid: text inside the <iframe> element
<iframe>Some content here</iframe>
<iframe><p>This is my embedded content.</p></iframe>
Both of these will trigger the "Text not allowed in element iframe in this context" error.
✅ Valid: using the src attribute
<iframesrc="https://example.com"title="Example website"></iframe>
This loads the document at the specified URL inside the iframe.
✅ Valid: using the srcdoc attribute
<iframesrcdoc="<p>This is my embedded content.</p>"title="Inline content"></iframe>
This renders the inline HTML provided in the srcdoc attribute. Note that HTML special characters must be escaped when the attribute value is enclosed in double quotes.
✅ Valid: self-closing style (empty element)
<iframesrc="https://example.com"title="Example website"></iframe>
The <iframe> element requires a closing tag, but nothing should appear between the opening and closing tags. Keeping the element empty is the correct approach.
✅ Valid: adding an accessible label with title
If your original intent was to describe the iframe's content for users or assistive technologies, use the title attribute:
<iframesrc="/embedded-report.html"title="Monthly sales report"></iframe>
The title attribute provides a label that screen readers can announce, improving the accessibility of the embedded content.
The box-shadow property applies one or more shadow effects to an element. Its syntax accepts several values in a flexible order:
box-shadow: <offset-x> <offset-y> <blur-radius>? <spread-radius>? <color>? inset?;
The <color> component is optional — if omitted, it defaults to currentcolor. However, when a color is provided, it must be a valid CSS color value. The validator raises this error when it encounters something in the color position that doesn't match any recognized color format.
Common causes of this error include:
- Misspelled color names — e.g.,
greyyinstead ofgrey, orbalckinstead ofblack. - Invalid hex codes — e.g.,
#GGGor#12345(hex codes must be 3, 4, 6, or 8 hex digits). - Fabricated color names — e.g.,
bananaordarkwhite, which are not part of the CSS named colors specification. - Malformed function syntax — e.g.,
rgb(0,0,0,0.3)using the legacy comma syntax with four values instead ofrgba(), or missing parentheses. - Incorrect value order — placing values in an unexpected position can cause the validator to misinterpret a non-color value as a color attempt.
This matters for standards compliance because browsers may silently ignore an invalid box-shadow declaration entirely, meaning your intended shadow effect won't render. Using valid CSS ensures consistent behavior across browsers and passes validation checks.
Recognized CSS color formats
The following formats are valid for the color component:
- Named colors:
red,blue,transparent,currentcolor, etc. - Hex codes:
#000,#0000,#000000,#00000080 - RGB/RGBA:
rgb(0, 0, 0),rgba(0, 0, 0, 0.5), or the modernrgb(0 0 0 / 50%) - HSL/HSLA:
hsl(0, 0%, 0%),hsla(0, 0%, 0%, 0.5), orhsl(0 0% 0% / 50%)
Examples
Incorrect — misspelled or invalid color values
<!-- "balck" is not a valid color name -->
<divstyle="box-shadow:2px4px8px balck;">Typo in color</div>
<!-- "banana" is not a recognized CSS color -->
<divstyle="box-shadow:2px4px8px banana;">Invalid color name</div>
<!-- "#12345" is not a valid hex code (5 digits) -->
<divstyle="box-shadow:2px4px8px#12345;">Bad hex code</div>
Correct — valid color values
<!-- Named color -->
<divstyle="box-shadow:2px4px8px black;">Named color</div>
<!-- Hex color -->
<divstyle="box-shadow:2px4px8px#333;">Hex color</div>
<!-- RGBA for semi-transparency -->
<divstyle="box-shadow:2px4px8pxrgba(0,0,0,0.3);">Semi-transparent</div>
<!-- HSL color -->
<divstyle="box-shadow:2px4px8pxhsl(210,50%,40%);">HSL color</div>
Correct — omitting the color entirely
If you want the shadow to inherit the element's text color, you can omit the color value altogether. This is valid and avoids the error:
<!-- Defaults to currentcolor -->
<divstyle="box-shadow:2px4px8px;">Uses currentcolor</div>
Correct — multiple shadows with valid colors
<divstyle="box-shadow:2px2px4pxrgba(0,0,0,0.2), inset 006px#ccc;">
Multiple shadows
</div>
To resolve this validation error, check the exact value the validator flags (the "X" in the error message), verify its spelling against the list of CSS named colors, and ensure any hex or function-based color uses correct syntax. If the color isn't needed, simply remove it and let the browser default to currentcolor.
Unicode allows some characters to be represented in multiple ways. For example, the accented letter "é" can be stored as a single precomposed character (U+00E9) or as two separate code points: the base letter "e" (U+0065) followed by a combining acute accent (U+0301). While these look identical when rendered, they are fundamentally different at the byte level. Unicode Normalization Form C (NFC) is the canonical form that prefers the single precomposed representation whenever one exists.
The HTML specification and the W3C Character Model for the World Wide Web require that all text in HTML documents be in NFC. This matters for several reasons:
- String matching and search: Non-NFC text can cause failures when browsers or scripts try to match strings, compare attribute values, or process CSS selectors. Two visually identical strings in different normalization forms won't match with simple byte comparison.
- Accessibility: Screen readers and assistive technologies may behave inconsistently when encountering decomposed character sequences.
- Interoperability: Different browsers, search engines, and tools may handle non-NFC text differently, leading to unpredictable behavior.
- Fragment identifiers and IDs: If an
idattribute contains non-NFC characters, fragment links (#id) may fail to work correctly.
This issue most commonly appears when text is copied from word processors, PDFs, or other applications that use decomposed Unicode forms (NFD), or when content is generated by software that doesn't normalize its output.
How to Fix It
- Identify the affected text: The validator will point to the specific line containing non-NFC characters. The characters will often look normal visually, so you'll need to inspect them at the code-point level.
- Convert to NFC: Use a text editor or command-line tool that supports Unicode normalization. Many programming languages provide built-in normalization functions.
- Prevent future issues: Configure your text editor or build pipeline to save files in NFC. When accepting user input, normalize it server-side before storing or embedding in HTML.
In Python, you can normalize a string:
importunicodedata
normalized=unicodedata.normalize('NFC', original_string)
In JavaScript (Node.js or browser):
constnormalized=originalString.normalize('NFC');
On the command line (using uconv from ICU):
uconv-xNFCinput.html>output.html
Examples
Incorrect (decomposed form — NFD)
In this example, the letter "é" is represented as two code points (e + combining acute accent), which triggers the validation warning. The source may look identical to the correct version, but the underlying bytes differ:
<!-- "é" here is stored as U+0065 U+0301 (decomposed) -->
<p>Résumé available upon request.</p>
Correct (precomposed form — NFC)
Here, the same text uses the single precomposed character é (U+00E9):
<!-- "é" here is stored as U+00E9 (precomposed) -->
<p>Résumé available upon request.</p>
Incorrect in attributes
Non-NFC text in attribute values also triggers this issue:
<!-- The id contains a decomposed character -->
<h2id="resumé">Résumé</h2>
Correct in attributes
<!-- The id uses the precomposed NFC character -->
<h2id="resumé">Résumé</h2>
While these examples look the same in rendered text, the difference is in how the characters are encoded. To verify your text is in NFC, you can paste it into a Unicode inspector tool or use the normalization functions mentioned above. For further reading, the W3C provides an excellent guide on Normalization in HTML and CSS.
A CSS color value (like a hex code or color name) was used where a background-image value is expected, or vice versa — the background-image property only accepts image functions such as url() or gradient functions, not plain color values.
The background-image property is specifically designed for setting images or gradients as backgrounds. If you want to set a solid background color, use the background-color property instead. Alternatively, you can use the shorthand background property, which accepts both colors and images.
This error often occurs when using the background shorthand incorrectly or when accidentally assigning a color value directly to background-image in inline styles.
Incorrect Example
<divstyle="background-image:#ff0000;">
This will trigger a validation error.
</div>
Correct Examples
Use background-color for solid colors:
<divstyle="background-color:#ff0000;">
Using background-color for a solid color.
</div>
Use background-image only for images or gradients:
<divstyle="background-image:url('banner.jpg');">
Using background-image with a URL.
</div>
<divstyle="background-image:linear-gradient(to right,#ff0000,#0000ff);">
Using background-image with a gradient.
</div>
Or use the background shorthand, which accepts both:
<divstyle="background:#ff0000url('banner.jpg') no-repeat center;">
Using the background shorthand.
</div>
The CSS font shorthand property has a specific syntax defined in the CSS specification. At minimum, it requires both a font-size and a font-family value. Optionally, you can prepend font-style, font-variant, and font-weight, and you can append a line-height value after the font-size using a slash separator. The full syntax looks like this:
font: [font-style] [font-variant] [font-weight] font-size[/line-height] font-family;
When the W3C validator reports that a value "is not a font-family value," it means the parser reached a point in the font declaration where it expected to find a font family name but instead found something it couldn't interpret as one. This often happens in two scenarios:
- Using
fontwhen you meant a specific property — For example, writingfont: 300when you only intended to set the font weight. The validator tries to parse300as a completefontvalue, and since there's nofont-sizeorfont-family, it fails. - Incomplete
fontshorthand — Providing some values but forgetting the mandatoryfont-familyat the end, such asfont: 300 16pxwithout a family name.
This matters because browsers may ignore an invalid font declaration entirely, causing your text to render with default or inherited styles instead of what you intended. Keeping your CSS valid also ensures consistent behavior across different browsers and helps maintain clean, predictable stylesheets.
How to fix it:
- If you only need to set a single font-related property, use the specific property (
font-weight,font-size,font-style,font-variant, orfont-family) instead of thefontshorthand. - If you want to use the
fontshorthand, make sure you include at leastfont-sizeandfont-family, and that all values appear in the correct order. - Remember that the
fontshorthand resets any omitted font sub-properties to their initial values, so use it deliberately.
Examples
Incorrect: Using font to set only the weight
<pstyle="font:300;">This text has an invalid font declaration.</p>
The validator reports that 300 is not a valid font-family value because the font shorthand expects at least a font-size and font-family.
Correct: Using font-weight directly
<pstyle="font-weight:300;">This text has a light font weight.</p>
Incorrect: Missing font-family in the shorthand
<pstyle="font: italic 30016px;">This is also invalid.</p>
Even though font-style, font-weight, and font-size are all present, the required font-family is missing.
Correct: Complete font shorthand
<pstyle="font: italic 30016px/1.5 'Helvetica', sans-serif;">This is valid.</p>
This includes all components in the correct order: font-style, font-weight, font-size/line-height, and font-family.
Correct: Minimal valid font shorthand
<pstyle="font:16px sans-serif;">Only size and family — the minimum required.</p>
Correct: Using individual properties instead of the shorthand
<pstyle="font-style: italic;font-weight:300;font-size:16px;line-height:1.5;font-family:'Helvetica', sans-serif;">
Each property set individually.
</p>
Using individual properties avoids the pitfalls of the shorthand and gives you explicit control without accidentally resetting other font sub-properties.
The role attribute is not allowed on a label element when that label is associated with a form control (a labelable element) through the for attribute or by nesting.
When a label is associated with a form control, the browser already understands its purpose — it's a label. Adding a role attribute overrides this native semantics, which is redundant at best and confusing for assistive technologies at worst.
A label becomes "associated" with a labelable element in two ways: explicitly via the for attribute pointing to the control's id, or implicitly by wrapping the control inside the label. Labelable elements include input (except type="hidden"), select, textarea, button, meter, output, and progress.
If the label is associated, simply remove the role attribute. The native semantics are already correct and sufficient.
If you truly need a custom role for some reason and the label is not functionally labeling a control, you can disassociate it by removing the for attribute or unnesting the control — but this is rarely the right approach.
Invalid Example
<labelfor="email"role="presentation">Email</label>
<inputtype="email"id="email">
Valid Example
<labelfor="email">Email</label>
<inputtype="email"id="email">
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