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 clip-path property defines a clipping region that controls which parts of an element are visible. It accepts a specific set of value types, and any deviation from the expected syntax will trigger a validation error. The validator checks your CSS against the specification and flags values that don’t conform.
The accepted value types for clip-path are:
- none — No clipping (the default).
- Basic shape functions — inset(), circle(), ellipse(), polygon(), and path().
- url() reference — Points to an SVG <clipPath> element, e.g., url(#myClip).
- Geometry-box keywords — border-box, padding-box, content-box, margin-box, fill-box, stroke-box, view-box. These can be used alone or combined with a basic shape.
Here are the most common mistakes that cause this error:
- Missing units on length values. Writing circle(50 at 50% 50%) is invalid because 50 needs a unit like px, em, or %. The value 0 is the only length that doesn’t require a unit.
- Wrong separators. In circle() and ellipse(), the position coordinates after at must be separated by spaces, not commas. In polygon(), each coordinate pair uses a space between x and y, while commas separate the points from each other.
- Malformed path() data. The SVG path string inside path() must be wrapped in quotes, e.g., path("M0 0 L100 0 L100 100 Z").
- Typos or unsupported functions. Using a function name the specification doesn’t define, or misspelling one like cirlce(), will trigger the error.
- Invalid polygon() fill-rule. If you specify a fill rule in polygon(), it must be nonzero or evenodd, followed by a comma before the first point.
Getting clip-path syntax right matters for standards compliance and cross-browser consistency. Browsers may silently ignore an invalid clip-path value, meaning your intended visual effect simply won’t appear — and you may not realize why. Validating your CSS catches these issues early.
Here is a quick reference for the correct syntax of each basic shape function:
- circle(<radius> at <cx> <cy>) — Radius is a length or percentage. Position uses spaces, not commas.
- ellipse(<rx> <ry> at <cx> <cy>) — Both radii are lengths or percentages.
- inset(<top> <right> <bottom> <left> round <border-radius>) — Offsets are lengths or percentages. The round keyword and border-radius are optional.
- polygon([<fill-rule>,] <x1> <y1>, <x2> <y2>, ...) — Space between x and y within a point; comma between points.
- path("<SVG path data>") — Path data string must be quoted.
Examples
Invalid clip-path values
<style>
/* Invalid: unitless radius; comma between position coordinates */
.clip-a {
clip-path: circle(50 at 50%, 50%);
}
/* Invalid: commas between x and y within each point */
.clip-b {
clip-path: polygon(0%, 0%, 100%, 0%, 100%, 100%, 0%, 100%);
}
/* Invalid: path data not wrapped in quotes */
.clip-c {
clip-path: path(M0 0 L100 0 L100 100 Z);
}
/* Invalid: misspelled function name */
.clip-d {
clip-path: cirlce(50% at 50% 50%);
}
</style>
Valid clip-path values
<style>
/* Valid circle: radius has a unit; position uses spaces */
.clip-a {
clip-path: circle(50px at 50% 50%);
}
/* Valid polygon: space between x and y, commas between points */
.clip-b {
clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
}
/* Valid path: SVG data is quoted */
.clip-c {
clip-path: path("M0 0 L100 0 L100 100 Z");
}
/* Valid inset with rounded corners */
.clip-d {
clip-path: inset(10% 20% 10% 20% round 8px);
}
/* Valid geometry-box keyword combined with a shape */
.clip-e {
clip-path: padding-box circle(50% at 50% 50%);
}
/* Valid: referencing an SVG clipPath */
.clip-f {
clip-path: url(#roundClip);
}
</style>
Full example with SVG <clipPath> reference
<!doctype html>
<html lang="en">
<head>
<title>Valid clip-path with SVG reference</title>
<style>
.clipped {
clip-path: url(#roundClip);
width: 200px;
height: 200px;
background: coral;
}
</style>
</head>
<body>
<svg width="0" height="0" aria-hidden="true">
<clipPath id="roundClip" clipPathUnits="objectBoundingBox">
<circle cx="0.5" cy="0.5" r="0.5"></circle>
</clipPath>
</svg>
<div class="clipped">Clipped element</div>
</body>
</html>
When you encounter this validation error, carefully check the value reported in the error message. Compare it against the accepted syntax for the function you’re using, paying close attention to units, separators, and quoting. Small syntax differences — a comma where a space should be, or a missing unit — are the most frequent culprits.
The font-size property defines the size of text in CSS and accepts a specific set of value types. When the W3C validator reports that a value “is not a font-size value,” it means the value you provided doesn’t match any of the accepted formats. Browsers may attempt to ignore or guess what you meant, but this leads to unpredictable rendering across different browsers and devices.
This error commonly occurs for a few reasons:
- Missing units on numeric values. Writing font-size: 16 instead of font-size: 16px. In CSS, unitless numbers (other than 0) are not valid lengths.
- Typos in units or keywords. For example, font-size: 16xp, font-size: 1.2erm, or font-size: lage.
- Using values from the wrong property. For example, font-size: bold (which belongs to font-weight) or font-size: center.
- Invalid or unsupported syntax. For example, font-size: 16 px (with a space between the number and unit) or font-size: auto (which is not valid for this property).
Valid font-size value types
| Type | Examples | Notes |
|---|---|---|
| Absolute keywords | xx-small, x-small, small, medium, large, x-large, xx-large, xxx-large | Browser-defined sizes |
| Relative keywords | smaller, larger | Relative to the parent element’s font size |
| Length units | 16px, 1.2em, 0.9rem, 12pt, 1vw | Must include a unit (except 0) |
| Percentages | 100%, 120%, 80% | Relative to the parent element’s font size |
| Math functions | calc(1rem + 2px), clamp(1rem, 2vw, 3rem) | CSS math expressions that resolve to a length |
Examples
Incorrect: missing unit on a number
<p style="font-size: 16;">This triggers a validation error.</p>
The value 16 is not valid because it lacks a CSS unit. Browsers may ignore this declaration entirely, leaving the text at its default or inherited size.
Correct: number with a valid unit
<p style="font-size: 16px;">This text has a valid font size.</p>
Incorrect: typo in the unit
<p style="font-size: 1.2erm;">Typo in the unit.</p>
Correct: proper em unit
<p style="font-size: 1.2em;">Correct em unit.</p>
Incorrect: value from the wrong property
<p style="font-size: bold;">Bold is not a font-size value.</p>
Correct: using a valid keyword
<p style="font-size: large;">Using a valid size keyword.</p>
Incorrect: space between number and unit
<p style="font-size: 16 px;">Space before the unit is invalid.</p>
Correct: no space between number and unit
<p style="font-size: 16px;">No space between number and unit.</p>
Full document example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Font Size Example</title>
<style>
.heading {
font-size: 2rem;
}
.body-text {
font-size: 1em;
}
.small-print {
font-size: 80%;
}
.responsive {
font-size: clamp(1rem, 2.5vw, 2rem);
}
</style>
</head>
<body>
<h1 class="heading">Valid heading size</h1>
<p class="body-text">Body text at 1em.</p>
<p class="small-print">Small print at 80%.</p>
<p class="responsive">Responsive text using clamp().</p>
</body>
</html>
To resolve this validation error, review every font-size declaration in your CSS or inline styles. Make sure each value is either a recognized keyword, a number immediately followed by a valid unit, a percentage, or a supported CSS function like calc(). If you intended 0, that is the one numeric value that does not require a unit — font-size: 0 is valid, though rarely useful in practice.
The aspect-ratio CSS property defines the preferred width-to-height ratio of an element’s box. Browsers use this ratio when calculating auto sizes and performing other layout functions, adjusting the element’s dimensions to maintain the specified proportion even as the parent container or viewport changes size.
The ratio is expressed as <width> / <height>. If the slash and height portion are omitted, height defaults to 1. So aspect-ratio: 2 is equivalent to aspect-ratio: 2 / 1. The property also accepts the auto keyword, which tells the element to use its intrinsic aspect ratio (if it has one), and a combined form like auto 3 / 4, which prefers the intrinsic ratio but falls back to the specified one.
This validation error typically occurs for several reasons:
- Using invalid separators or syntax, such as a colon (:) instead of a slash (/), e.g., aspect-ratio: 16:9.
- Providing units, such as aspect-ratio: 16px / 9px. The values must be unitless positive numbers.
- Using zero or negative numbers, which are not valid. Both parts of the ratio must be positive (> 0).
- Providing a string or unrecognized keyword, such as aspect-ratio: wide or aspect-ratio: "16/9".
- Missing spaces around the slash, though this is less common — 16/9 may work in browsers but the canonical form uses spaces: 16 / 9.
- Using the property in inline style attributes validated against an older CSS level where aspect-ratio wasn’t yet recognized by the validator.
Getting this value right matters for layout consistency across browsers. An invalid value will be ignored entirely by the browser, meaning the element won’t maintain any aspect ratio, potentially breaking your design. It’s especially important for responsive images, video containers, and card layouts where maintaining proportions is critical.
Examples
Incorrect: using a colon as the separator
<div style="aspect-ratio: 16:9; width: 100%;"></div>
The colon syntax (common in video specifications) is not valid CSS. The validator will reject 16:9 as an aspect-ratio value.
Incorrect: using units in the ratio
<div style="aspect-ratio: 16px / 9px; width: 100%;"></div>
The ratio values must be unitless numbers. Adding px or any other unit makes the value invalid.
Incorrect: using zero in the ratio
<div style="aspect-ratio: 0 / 1; width: 100%;"></div>
Both numbers in the ratio must be strictly positive. Zero is not allowed.
Correct: standard ratio with a slash
<div style="aspect-ratio: 16 / 9; width: 100%;"></div>
Correct: single number (height defaults to 1)
<div style="aspect-ratio: 2; width: 100%;"></div>
This is equivalent to aspect-ratio: 2 / 1.
Correct: square ratio
<div style="aspect-ratio: 1 / 1; width: 100%;"></div>
Correct: using the auto keyword
<img src="photo.jpg" alt="A landscape photo" style="aspect-ratio: auto; width: 100%;">
The element uses its intrinsic aspect ratio if available.
Correct: combining auto with a fallback ratio
<img src="photo.jpg" alt="A landscape photo" style="aspect-ratio: auto 4 / 3; width: 100%;">
The browser prefers the image’s intrinsic ratio, but if it hasn’t loaded yet or has no intrinsic ratio, it falls back to 4 / 3. This is useful for preventing layout shift while images load.
Correct: using global CSS values
<div style="aspect-ratio: inherit; width: 100%;"></div>
Global values like inherit, initial, unset, revert, and revert-layer are also valid.
Every HTML element has a defined set of attributes it accepts. The HTML specification maintains strict rules about which attributes belong on which elements. For example, the href attribute is valid on an <a> element but not on a <div>. The for attribute belongs on <label> and <output> elements but not on <span>. When you place an attribute on an element that doesn’t recognize it, the validator flags the error.
This issue matters for several reasons. First, browsers may silently ignore unrecognized attributes, meaning your code might appear to work but isn’t actually doing anything — leading to hard-to-diagnose bugs. Second, assistive technologies like screen readers rely on valid HTML to correctly interpret page structure and behavior. Invalid attributes can confuse these tools and degrade accessibility. Third, standards-compliant HTML ensures consistent behavior across all browsers and future-proofs your code.
There are several common causes of this error:
- Typos or misspellings — Writing hieght instead of height, or scr instead of src.
- Attributes on the wrong element — Using placeholder on a <div> instead of an <input> or <textarea>.
- Obsolete attributes — Using presentational attributes like align, bgcolor, or border that have been removed from the HTML specification in favor of CSS.
- Framework-specific attributes — Using attributes like ng-click (Angular), v-if (Vue), or @click (Vue shorthand) that aren’t part of standard HTML. These frameworks typically process them before the browser sees them, but the raw HTML won’t validate.
- Custom attributes without the data-* prefix — Inventing your own attributes like tooltip or status without following the data-* convention.
- ARIA attributes with typos — Writing aria-role instead of the correct role, or aria-labelled instead of aria-labelledby.
Examples
Attribute used on the wrong element
The placeholder attribute is only valid on <input> and <textarea> elements:
<!-- ❌ "placeholder" not allowed on "div" -->
<div placeholder="Enter text here">Content</div>
<!-- ✅ Use placeholder on a supported element -->
<input type="text" placeholder="Enter text here">
Obsolete presentational attribute
The align attribute has been removed from most elements in HTML5. Use CSS instead:
<!-- ❌ "align" not allowed on "div" -->
<div align="center">Centered content</div>
<!-- ✅ Use CSS for presentation -->
<div style="text-align: center;">Centered content</div>
Custom attribute without data-* prefix
If you need to store custom data on an element, use the data-* attribute format:
<!-- ❌ "tooltip" not allowed on "span" -->
<span tooltip="More information">Hover me</span>
<!-- ✅ Use a data-* attribute for custom data -->
<span data-tooltip="More information">Hover me</span>
The data-* attributes are specifically designed for embedding custom data. You can access them in JavaScript via the dataset property, e.g., element.dataset.tooltip.
Misspelled attribute
A simple typo can trigger this error:
<!-- ❌ "widht" not allowed on "img" -->
<img src="photo.jpg" widht="300" alt="A photo">
<!-- ✅ Correct the spelling -->
<img src="photo.jpg" width="300" alt="A photo">
ARIA attribute typo
ARIA attributes must match their exact specification names:
<!-- ❌ "aria-labelled" not allowed on "input" -->
<input type="text" aria-labelled="name-label">
<!-- ✅ Use the correct ARIA attribute name -->
<input type="text" aria-labelledby="name-label">
Framework-specific attributes
If you’re using a JavaScript framework and want your source templates to validate, be aware that framework-specific syntax won’t pass validation. In Vue, for example, you can use the data-* equivalent or accept that templates are preprocessed:
<!-- ❌ "v-if" not allowed on "div" -->
<div v-if="isVisible">Hello</div>
<!-- This is expected with Vue templates and is typically not a concern,
since the framework processes these before they reach the browser. -->
When encountering this error, check the MDN Web Docs reference for the element in question to see which attributes it actually supports. This will quickly clarify whether you need to fix a typo, move the attribute to a different element, replace it with CSS, or convert it to a data-* attribute.
The font-size property sets the size of text and expects a valid CSS value — either a keyword (like small, medium, large), a numeric value with a unit (like 16px, 1.2em, 100%), or 0 (which needs no unit). When the validator reports that "px" is not a valid font-size value, it means the property received the string px alone, without the required number preceding it.
This issue commonly arises in a few scenarios:
- Typos or accidental deletion: The number was removed during editing, leaving just the unit behind.
- Template or CMS output: A dynamic value (e.g., from a variable or database field) resolved to empty, producing something like font-size: px;.
- Preprocessor errors: A Sass, Less, or other CSS preprocessor variable was undefined or empty, resulting in a bare unit in the compiled CSS.
While most browsers will simply ignore the invalid declaration and fall back to an inherited or default font size, this creates unpredictable rendering. The text may appear at an unexpected size, and the behavior may differ across browsers. Invalid CSS also causes W3C validation failures, which can signal deeper problems in your code or build pipeline.
How to Fix It
- Check for a missing number: Look at the font-size declaration flagged by the validator and add the intended numeric value before the unit.
- Inspect dynamic values: If the value comes from a variable, template engine, or CMS field, ensure it outputs a complete value (e.g., 16 or 16px) rather than an empty string.
- Use a fallback: When generating CSS dynamically, consider providing a fallback value in case the variable is empty.
Examples
❌ Incorrect: Bare unit with no number
<p style="font-size: px;">This text has an invalid font size.</p>
The validator will report that "px" is not a valid font-size value because no number precedes the unit.
✅ Correct: Numeric value with unit
<p style="font-size: 16px;">This text has a valid font size.</p>
❌ Incorrect: Empty value from a template variable
This is a common pattern in templating systems that can produce invalid CSS:
<p style="font-size: px;">Dynamic content here.</p>
When the template variable resolves to an empty string, only px remains.
✅ Correct: Using a keyword value
If you don’t need a specific pixel size, CSS keywords are another valid option:
<p style="font-size: medium;">This uses a keyword font size.</p>
✅ Correct: Using other valid units
The font-size property accepts many unit types. All of these are valid:
<style>
.example-em { font-size: 1.2em; }
.example-rem { font-size: 1rem; }
.example-percent { font-size: 120%; }
.example-px { font-size: 14px; }
.example-keyword { font-size: large; }
</style>
✅ Correct: Full document example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Font Size Example</title>
<style>
p {
font-size: 16px;
}
</style>
</head>
<body>
<p>This paragraph has a valid font size of 16px.</p>
</body>
</html>
The HTML specification defines a specific set of global attributes (like class, id, title, style, etc.) that are allowed on all elements, plus element-specific attributes for certain tags. Any attribute that isn’t part of these standard sets and doesn’t follow the data-* custom attribute convention is considered invalid and will trigger a validation error.
This issue is especially common with older integrations of third-party tools like ShareThis, AddThis, or similar social sharing widgets, particularly when embedded through a CMS like Drupal or WordPress. These older implementations relied on proprietary attributes such as displayText, st_url, and st_title directly on <span> or other elements. Modern versions of these tools have since migrated to valid data-* attributes.
Why This Matters
- Standards compliance: Non-standard attributes violate the HTML specification, meaning your markup is technically invalid and may behave unpredictably across browsers.
- Forward compatibility: Browsers could introduce a native displaytext attribute in the future with entirely different behavior, causing conflicts with your code.
- Accessibility: Assistive technologies rely on well-formed HTML. Non-standard attributes can confuse screen readers or other tools that parse the DOM.
- Maintainability: Using the standardized data-* convention makes it immediately clear to other developers that an attribute holds custom data, improving code readability.
How to Fix It
- Identify all non-standard attributes on your elements (e.g., displayText, st_url, st_title).
- Prefix each one with data- to convert it into a valid custom data attribute (e.g., data-displaytext, data-st-url, data-st-title).
- Update any JavaScript that references these attributes. If JavaScript accesses them via element.getAttribute('displayText'), change it to element.getAttribute('data-displaytext') or use the dataset API (element.dataset.displaytext).
- If using a CMS or third-party plugin, update the plugin or module to its latest version, which likely uses valid data-* attributes already.
Note that data-* attribute names should be all lowercase. Even if the original attribute used camelCase like displayText, the corrected version should be data-displaytext.
Examples
Invalid: Non-standard attribute on a <span>
<span class="st_sharethis" displaytext="ShareThis" st_url="https://example.com" st_title="My Page">
Share
</span>
This triggers validation errors for displaytext, st_url, and st_title because none of these are valid HTML attributes.
Valid: Using data-* attributes
<span class="st_sharethis" data-displaytext="ShareThis" data-st-url="https://example.com" data-st-title="My Page">
Share
</span>
Accessing data-* attributes in JavaScript
If your JavaScript relied on the old attribute names, update the references:
<span id="share-btn" data-displaytext="ShareThis">Share</span>
<script>
const btn = document.getElementById('share-btn');
// Using getAttribute
const text = btn.getAttribute('data-displaytext');
// Using the dataset API
const textAlt = btn.dataset.displaytext;
</script>
Valid: Using a standard attribute instead
In some cases, the intent of displaytext is simply to provide a label or tooltip. If so, a standard attribute like title may be more appropriate:
<span class="st_sharethis" title="ShareThis">Share</span>
Choose the approach that best matches your use case — data-* for custom data consumed by JavaScript, or a semantic HTML attribute if one already serves the purpose.
The border-color property sets the color of an element’s four borders. When the W3C validator reports that a given value “is not a border-color value,” it means the value you provided doesn’t match any recognized CSS color format. Common mistakes that trigger this error include using a bare number like 0 instead of a color, misspelling a color keyword (e.g., grren instead of green), forgetting the # prefix on a hex code, or passing an invalid argument to a color function.
This matters because browsers handle invalid CSS values unpredictably. When a browser encounters an unrecognized border-color value, it discards the entire declaration and falls back to the inherited or initial value (typically currentcolor). This can lead to inconsistent rendering across browsers and make your design behave in unexpected ways. Writing valid CSS ensures predictable, cross-browser results and keeps your stylesheets maintainable.
Valid color formats
The CSS border-color property accepts any valid <color> value, including:
- Named keywords — red, blue, transparent, currentcolor, etc.
- Hexadecimal — #rgb, #rrggbb, #rgba, #rrggbbaa
- rgb() / rgba() — rgb(255, 0, 0) or rgb(255 0 0 / 50%)
- hsl() / hsla() — hsl(0, 100%, 50%) or hsl(0 100% 50% / 0.5)
You can also specify one to four color values to target individual sides (top, right, bottom, left), following the standard CSS shorthand pattern.
Examples
Invalid: bare number instead of a color
A number like 0 is not a valid color value and triggers the error:
<style>
.box {
border: 1px solid;
border-color: 0;
}
</style>
Invalid: misspelled color keyword
Typos in color names are not recognized by CSS:
<style>
.box {
border: 1px solid;
border-color: grren;
}
</style>
Invalid: hex code missing the # prefix
Without the leading #, the value is treated as an unknown keyword:
<style>
.box {
border: 1px solid;
border-color: ff0000;
}
</style>
Fixed: using a named color keyword
<style>
.box {
border: 1px solid;
border-color: green;
}
</style>
Fixed: using a hexadecimal value
<style>
.box {
border: 1px solid;
border-color: #00ff00;
}
</style>
Fixed: using rgb() functional notation
<style>
.box {
border: 1px solid;
border-color: rgb(0, 128, 0);
}
</style>
Fixed: using hsl() functional notation
<style>
.box {
border: 1px solid;
border-color: hsl(120, 100%, 25%);
}
</style>
Fixed: setting different colors per side
You can provide up to four valid color values to control each border individually (top, right, bottom, left):
<style>
.box {
border: 1px solid;
border-color: red green blue orange;
}
</style>
Fixed: using transparent or currentcolor
The special keywords transparent and currentcolor are also valid:
<style>
.box {
border: 1px solid;
border-color: transparent;
}
.highlight {
color: navy;
border: 2px solid;
border-color: currentcolor;
}
</style>
If you’re unsure whether a value is a valid CSS color, check the MDN <color> data type reference for the complete list of accepted formats.
The mask-image CSS property sets one or more mask layers for an element, controlling which parts are visible based on the mask’s alpha channel or luminance. According to the CSS Masking specification, the property accepts a comma-separated list of mask references, where each individual value must be one of:
- none — No mask layer is applied.
- A <image> value — This includes url() references to image files (PNG, SVG, etc.) and CSS image functions like image().
- A CSS gradient — Functions like linear-gradient(), radial-gradient(), conic-gradient(), and their repeating variants (repeating-linear-gradient(), etc.).
When the validator encounters a value that doesn’t match any of these accepted forms, it flags the error. This matters because browsers will silently discard invalid mask-image declarations, meaning your intended masking effect won’t apply, and the element will render as if no mask were set.
Common causes
Typos in gradient or function names are a frequent trigger. For example, writing linear-gradiant() instead of linear-gradient(), or radial-grad() instead of radial-gradient().
Bare image paths without url() will also cause this error. The value mask.png is not valid on its own — it must be wrapped as url('mask.png').
Unsupported keywords or arbitrary strings like mask-image: circle or mask-image: overlay are not valid. The only keyword mask-image accepts is none.
Malformed gradient syntax such as missing parentheses, invalid color stops, or incorrect direction keywords can also produce this error. For instance, linear-gradient(right, red, blue) is invalid because directional keywords require the to prefix.
Vendor-prefixed values used without the matching prefix on the property can trigger issues as well. Using -webkit-gradient() as a value for the standard mask-image property may not validate.
Examples
Incorrect: bare image path without url()
<div style="mask-image: mask.png;">
Content here
</div>
Correct: image path wrapped in url()
<div style="mask-image: url('mask.png');">
Content here
</div>
Incorrect: typo in gradient function name
<div style="mask-image: linear-gradiant(to right, transparent, black);">
Content here
</div>
Correct: properly spelled gradient function
<div style="mask-image: linear-gradient(to right, transparent, black);">
Content here
</div>
Incorrect: missing to keyword in gradient direction
<div style="mask-image: linear-gradient(right, transparent, black);">
Content here
</div>
Correct: direction uses the to keyword
<div style="mask-image: linear-gradient(to right, transparent, black);">
Content here
</div>
Incorrect: unsupported keyword
<div style="mask-image: overlay;">
Content here
</div>
Correct: using none to explicitly disable masking
<div style="mask-image: none;">
Content here
</div>
Correct: multiple mask layers
<div style="mask-image: url('star.svg'), linear-gradient(to bottom, black, transparent);">
Content here
</div>
Correct: radial gradient as a mask
<div style="mask-image: radial-gradient(circle, black 50%, transparent 100%);">
Content here
</div>
Note that browser support for the unprefixed mask-image property has improved significantly, but some older browsers may still require the -webkit-mask-image prefix. When using the prefixed version, make sure to also include the standard property for forward compatibility. The W3C validator checks against the standard syntax, so always ensure your standard mask-image declaration uses valid values even if you also include prefixed versions.
The ontouchstart attribute is an inline event handler that some developers use to detect when a user touches an element on a touchscreen device. However, unlike standard event handler attributes such as onclick, onmousedown, or onkeydown, the ontouchstart attribute is not defined in the HTML specification. The W3C validator flags it because only attributes explicitly listed in the spec are allowed on a given element.
The touchstart event itself is a legitimate event defined in the Touch Events specification — it’s the inline HTML attribute form (ontouchstart) that isn’t recognized as a valid attribute by the HTML standard. This distinction is important: you can absolutely listen for touchstart events, just not via an inline attribute on an HTML element.
Why This Is a Problem
- Standards compliance: Using non-standard attributes produces invalid HTML, which can cause issues with automated testing, accessibility audits, and content management pipelines that enforce validation.
- Accessibility: Relying solely on touch events excludes users who navigate with a keyboard, mouse, or assistive technology. Touch-only handlers can make interactive elements completely inaccessible to non-touch users.
- Maintainability: Inline event handlers mix behavior with markup, making code harder to maintain and debug compared to centralized JavaScript event listeners.
- Browser inconsistencies: While most mobile browsers support the touchstart event, the inline attribute form is not guaranteed to work consistently across all environments.
How to Fix It
- Remove the inline ontouchstart attribute from your HTML element.
- Use addEventListener in JavaScript to attach the touchstart event listener programmatically.
- Consider also handling click or pointerdown events so the interaction works for all input types — mouse, keyboard, touch, and stylus. The Pointer Events API (pointerdown, pointerup, etc.) is a modern, unified approach that covers mouse, touch, and pen input in a single event model.
Examples
❌ Invalid: Inline ontouchstart attribute
<div ontouchstart="handleTouch()">Tap me</div>
This triggers the validation error because ontouchstart is not a recognized attribute in the HTML specification.
✅ Valid: Using addEventListener for touchstart
<div id="touch-target">Tap me</div>
<script>
document.getElementById("touch-target").addEventListener("touchstart", function(event) {
// Handle touch interaction
});
</script>
The touchstart event is attached through JavaScript, keeping the HTML valid and clean.
✅ Valid: Using Pointer Events for cross-input support
<button id="action-btn" type="button">Tap or click me</button>
<script>
document.getElementById("action-btn").addEventListener("pointerdown", function(event) {
// Handles mouse, touch, and pen input
});
</script>
Using pointerdown instead of touchstart provides a single handler that works across all input types. Note also the use of a <button> element, which is natively focusable and accessible, making it a better choice than a <div> for interactive elements.
✅ Valid: Supporting both touch and non-touch with separate listeners
<div id="interactive-area" role="button" tabindex="0">Interact with me</div>
<script>
var el = document.getElementById("interactive-area");
el.addEventListener("touchstart", function(event) {
// Handle touch-specific interaction
});
el.addEventListener("click", function(event) {
// Handle mouse and keyboard interaction
});
</script>
If you must use a <div> as an interactive element, add role="button" and tabindex="0" for accessibility, and attach both touchstart and click listeners to cover all input methods.
The <table> element in HTML supports a limited set of attributes — primarily global attributes like class, id, and style. The height attribute was never part of the HTML standard for tables (unlike width, which was valid in HTML 4 but has since been deprecated). Despite this, many browsers historically accepted height on <table> as a non-standard extension, which led to its widespread but incorrect use.
Because height is not a recognized attribute on <table>, using it means your markup is invalid and may behave inconsistently across browsers. Some browsers might honor it, others might ignore it entirely, and future browser versions could change their behavior at any time. Relying on non-standard attributes makes your code fragile and harder to maintain.
The fix is straightforward: remove the height attribute from the <table> element and use CSS to set the desired height. You can apply the CSS inline via the style attribute, or better yet, use an external or internal stylesheet with a class or ID selector.
Examples
❌ Invalid: height attribute on <table>
<table height="300">
<tr>
<td>Name</td>
<td>Score</td>
</tr>
<tr>
<td>Alice</td>
<td>95</td>
</tr>
</table>
This triggers the validator error: Attribute “height” not allowed on element “table” at this point.
✅ Fixed: Using inline CSS
<table style="height: 300px;">
<tr>
<td>Name</td>
<td>Score</td>
</tr>
<tr>
<td>Alice</td>
<td>95</td>
</tr>
</table>
✅ Fixed: Using a CSS class (preferred)
<style>
.tall-table {
height: 300px;
}
</style>
<table class="tall-table">
<tr>
<td>Name</td>
<td>Score</td>
</tr>
<tr>
<td>Alice</td>
<td>95</td>
</tr>
</table>
Using a class keeps your HTML clean and makes it easy to adjust the styling later without touching the markup. Note that min-height is often a better choice than height for tables, since table content can naturally grow beyond a fixed height, and min-height ensures the table is at least a certain size without clipping content.
The <time> element represents a specific moment or duration in time. Browsers, search engines, and assistive technologies rely on parsing its value to understand temporal data programmatically. The element can get its machine-readable value from two places: the datetime attribute, or, if that attribute is absent, from the element’s text content directly.
When there is no datetime attribute, the text content itself must be in one of the valid formats specified by the HTML standard. This is where the error typically occurs—authors write a human-readable date like “March 20, 2025” or “last Tuesday” as the text content without providing a datetime attribute, and the validator rejects it because that string isn’t machine-parsable.
Why This Matters
- Machine readability: Search engines (via structured data) and browser features (like calendar integration) depend on parsing the <time> element’s value. An invalid format means these tools can’t understand the date or time.
- Accessibility: Screen readers and other assistive technologies may use the machine-readable datetime to present temporal information more helpfully to users.
- Standards compliance: The HTML specification explicitly defines which formats are valid. Using anything else makes your document non-conforming.
How to Fix It
You have two options:
- Add a datetime attribute with the machine-readable value, and keep the human-readable text as the visible content. This is the most common and practical approach.
- Use a valid format directly as the text content if you don’t mind displaying a machine-readable format to users.
Valid Formats
Here is a reference of accepted formats for the <time> element:
| Type | Example(s) |
|---|---|
| Valid year | 2011 |
| Valid month | 2011-11 |
| Valid date | 2011-11-18 |
| Valid yearless date | 11-18 |
| Valid week | 2011-W47 |
| Valid time | 14:54, 14:54:39, 14:54:39.929 |
| Valid local date and time | 2011-11-18T14:54:39.929 or 2011-11-18 14:54:39.929 |
| Valid global date and time | 2011-11-18T14:54:39.929Z, 2011-11-18T14:54:39.929-04:00 |
| Valid duration | PT4H18M3S, P2D (2 days), P3DT4H (3 days, 4 hours) |
Examples
Incorrect: Human-readable text without datetime attribute
The validator will report the error because “March 20, 2025” is not a valid machine-readable format:
<p>The concert is on <time>March 20, 2025</time>.</p>
Incorrect: Informal text as content
Similarly, casual date strings are not valid:
<p>Updated <time>last Friday</time>.</p>
Correct: Using the datetime attribute
Add a datetime attribute with the machine-readable value and keep the human-friendly text visible:
<p>The concert is on <time datetime="2025-03-20">March 20, 2025</time>.</p>
Correct: Including time and timezone
<p>
The event starts at
<time datetime="2025-03-20T13:00-05:00">1:00 PM EST on March 20, 2025</time>.
</p>
Correct: Machine-readable format as text content
If no datetime attribute is provided, the text content itself must be a valid format:
<p>Date: <time>2025-03-20</time></p>
Correct: Representing a duration
<p>Cooking time: <time datetime="PT1H30M">1 hour and 30 minutes</time>.</p>
Correct: Using just a time value
<p>The shop opens at <time>09:00</time> every day.</p>
As a general rule, whenever you want to display a date or time in a natural, human-friendly way, always pair it with a datetime attribute that contains the machine-readable equivalent. This keeps your HTML valid, your content accessible, and your temporal data useful to machines.
HTML has a defined set of global attributes (like class, id, title, lang, etc.) and element-specific attributes that are permitted by the specification. Any attribute that falls outside this set — such as st_url, st_title, or displayText — will trigger a validation error because the browser and the validator don’t recognize it as part of the HTML standard.
The st_url attribute, along with related attributes like st_title, st_via, and displayText, originated from early versions of the ShareThis social sharing library. These were proprietary attributes that the ShareThis JavaScript would read from the DOM to configure sharing behavior. While browsers generally ignore attributes they don’t understand (so the page may still appear to work), using non-standard attributes violates the HTML specification and can cause several problems:
- Standards compliance: Invalid HTML can lead to unpredictable behavior across different browsers and future browser versions.
- Accessibility: Screen readers and other assistive technologies may not handle non-standard attributes correctly, potentially causing confusion.
- Maintainability: Non-standard markup is harder for other developers to understand and maintain.
- SEO: Search engine crawlers may penalize or misinterpret pages with significant validation errors.
HTML5 introduced data-* attributes specifically to solve this problem. Any custom data you need to attach to an element can use this pattern — for example, data-st-url instead of st_url. The ShareThis library itself updated its integration to use data-* attributes in later versions, so modern implementations should already follow this pattern.
How to Fix
- Replace proprietary attributes with data-* equivalents: Convert st_url to data-st-url, st_title to data-st-title, displayText to data-display-text, and so on.
- Update your ShareThis integration: If you’re using an outdated version of ShareThis (or an outdated CMS module/plugin like an old Drupal integration), update to the latest version, which uses valid HTML5 attributes.
- Check your CMS templates: If the invalid attributes are hardcoded in a theme template or a content block, update them manually.
Examples
Invalid: Proprietary attributes on a <span>
<span class="st_facebook_large" displayText="Facebook" st_url="https://example.com" st_title="My Page Title"></span>
<span class="st_twitter_large" displayText="Twitter" st_url="https://example.com" st_title="My Page Title"></span>
This markup uses st_url, st_title, and displayText, none of which are valid HTML attributes.
Valid: Using data-* attributes instead
<span class="st_facebook_large" data-display-text="Facebook" data-st-url="https://example.com" data-st-title="My Page Title"></span>
<span class="st_twitter_large" data-display-text="Twitter" data-st-url="https://example.com" data-st-title="My Page Title"></span>
By prefixing each custom attribute with data-, the markup becomes valid HTML5. Note that you may also need to update the associated JavaScript to read from these new attribute names (e.g., using element.dataset.stUrl instead of element.getAttribute('st_url')).
Valid: Modern ShareThis integration
If you’re updating your ShareThis integration entirely, the modern approach uses a different markup pattern:
<div class="sharethis-inline-share-buttons" data-url="https://example.com" data-title="My Page Title"></div>
Modern versions of the ShareThis library expect data-* attributes by default, so upgrading the library and its associated markup is the cleanest solution. Check the ShareThis documentation for the latest recommended integration approach for your platform.
HTML has a defined set of global attributes (such as id, class, lang, and title) and element-specific attributes. Any attribute that isn’t part of these recognized sets will trigger a validation error. The st_title attribute is a proprietary, non-standard attribute that was used by older versions of the ShareThis sharing widget to pass metadata — specifically, the title of the content being shared.
The HTML5 specification introduced data-* attributes as the standard mechanism for embedding custom data on elements. These attributes allow developers to store arbitrary information without conflicting with the HTML spec. Newer versions of ShareThis and similar services have adopted this convention, but legacy code — especially in CMS themes, plugins, or modules — may still use the old non-standard format.
Using invalid attributes causes several problems:
- Standards compliance: The document fails W3C validation, which can indicate deeper markup quality issues.
- Future compatibility: Browsers are not required to handle non-standard attributes in any predictable way. Future browser updates could ignore or strip them.
- Maintainability: Non-standard attributes make code harder for other developers to understand and maintain.
- Accessibility tools: Screen readers and other assistive technologies rely on well-formed HTML. Invalid attributes can cause unexpected behavior in these tools.
To fix this, replace all proprietary ShareThis attributes with their data-* equivalents. For example, st_title becomes data-st-title, st_url becomes data-st-url, and displayText becomes data-st-displaytext. You should also update the ShareThis JavaScript library to a version that recognizes the new attribute format.
Examples
❌ Invalid: Using proprietary attributes
<span class="st_sharethis" st_title="My Article" st_url="https://example.com/article" displayText="ShareThis">
Share
</span>
This triggers validation errors for st_title, st_url, and displayText because none of these are valid HTML attributes.
✅ Valid: Using data-* attributes
<span class="st_sharethis" data-st-title="My Article" data-st-url="https://example.com/article" data-st-displaytext="ShareThis">
Share
</span>
All custom data is now stored in properly namespaced data-* attributes, which are fully compliant with the HTML5 specification.
✅ Valid: Using a button element with data-* attributes
If the element is interactive (e.g., it triggers a share action on click), consider using a <button> instead of a <span> for better accessibility:
<button type="button" class="st_sharethis" data-st-title="My Article" data-st-url="https://example.com/article">
Share this article
</button>
Fixing this in a CMS
If you’re using Drupal, WordPress, or another CMS with a ShareThis module or plugin:
- Update the plugin/module to the latest version — most have already migrated to data-* attributes.
- Check your theme templates for hardcoded ShareThis markup that may still use the old attribute format.
- Search your codebase for st_title, st_url, and displayText and replace them with data-st-title, data-st-url, and data-st-displaytext respectively.
- Update the ShareThis JavaScript to a version compatible with the new attribute names, and verify that sharing functionality still works after the change.
The correct value is nowrap (without a hyphen), not no-wrap.
The flex-wrap CSS property controls whether flex items are forced onto a single line or can wrap onto multiple lines. It accepts three values: nowrap (the default), wrap, and wrap-reverse. A common mistake is writing no-wrap with a hyphen, likely because the white-space CSS property uses nowrap and no-wrap interchangeably in some contexts, or simply because it looks more natural in English. However, for flex-wrap, only the unhyphenated nowrap is valid.
HTML Example With the Issue
<div style="display: flex; flex-wrap: no-wrap;">
<p>Item 1</p>
<p>Item 2</p>
</div>
Fixed HTML Example
<div style="display: flex; flex-wrap: nowrap;">
<p>Item 1</p>
<p>Item 2</p>
</div>
The min-height property sets the minimum height of an element. Unlike shorthand properties such as margin or padding, min-height accepts only a single value. Providing multiple space-separated values (e.g., min-height: 100px 200px) is invalid and will trigger this error.
This error commonly occurs for several reasons:
- Multiple values provided: min-height is not a shorthand and does not accept more than one value.
- Invalid units or typos: Using an unrecognized unit (e.g., 100pixels instead of 100px) or a misspelled keyword.
- Using unsupported CSS functions or syntax: Some newer CSS features like min-height: fit-content(200px) may not be recognized by the validator or may lack browser support.
- Confusing min-height with other properties: Accidentally using syntax meant for properties like grid-template-rows or minmax() expressions.
- Missing units on non-zero values: Writing min-height: 100 instead of min-height: 100px. Zero is the only numeric value that doesn’t require a unit.
According to the CSS specification, valid values for min-height include:
| Value Type | Examples |
|---|---|
| Length | 0, 100px, 10em, 5rem, 50vh |
| Percentage | 50%, 100% |
| Keywords | auto, min-content, max-content, none |
| Functions | fit-content, calc(100vh - 50px) |
Fixing this issue ensures your CSS is standards-compliant and behaves predictably across browsers. Invalid min-height values will be ignored by browsers, which means your layout may not render as intended.
Examples
Incorrect: multiple values
<div style="min-height: 100px 200px;">Content</div>
min-height only accepts a single value. This is not a shorthand property.
Incorrect: missing unit
<div style="min-height: 100;">Content</div>
Non-zero numeric values must include a unit.
Incorrect: invalid keyword or typo
<div style="min-height: inheret;">Content</div>
The keyword inherit is misspelled.
Correct: single length value
<div style="min-height: 100px;">Content</div>
Correct: percentage value
<div style="min-height: 50%;">Content</div>
Correct: using calc() for computed values
<div style="min-height: calc(100vh - 80px);">Content</div>
Correct: using a keyword
<div style="min-height: min-content;">Content</div>
Correct: using auto
<div style="min-height: auto;">Content</div>
If you need to set both a minimum and maximum height on an element, use min-height and max-height as separate properties:
<div style="min-height: 100px; max-height: 400px;">Content</div>
The async attribute tells the browser to download and execute a script without blocking HTML parsing. For external scripts (those with a src attribute), this means the browser can continue parsing the page while fetching the file, then execute the script as soon as it’s available. For inline module scripts (type="module"), async changes how the module’s dependency graph is handled — the module and its imports execute as soon as they’re all ready, rather than waiting for HTML parsing to complete.
For a classic inline script (no src, no type="module"), there is nothing to download asynchronously. The browser encounters the code directly in the HTML and executes it immediately. Applying async in this context is meaningless and contradicts the HTML specification, which is why the W3C validator flags it as an error.
Beyond standards compliance, using async incorrectly can signal a misunderstanding of script loading behavior, which may lead to bugs. For example, a developer might mistakenly believe that async on an inline script will defer its execution, when in reality it has no effect and the script still runs synchronously during parsing.
How to Fix
You have several options depending on your intent:
- If the script should be external, move the code to a separate file and reference it with the src attribute alongside async.
- If the script should be an inline module, add type="module" to the <script> tag. Note that module scripts are deferred by default, and async makes them execute as soon as their dependencies are resolved rather than waiting for parsing to finish.
- If the script is a plain inline script, simply remove the async attribute — it has no practical effect anyway.
Examples
❌ Invalid: async on a classic inline script
<script async>
console.log("Hello, world!");
</script>
This triggers the validator error because there is no src attribute and the type is not "module".
✅ Fixed: Remove async from the inline script
<script>
console.log("Hello, world!");
</script>
✅ Fixed: Use async with an external script
<script async src="app.js"></script>
The async attribute is valid here because the browser needs to fetch app.js from the server, and async controls when that downloaded script executes relative to parsing.
✅ Fixed: Use async with an inline module
<script async type="module">
import { greet } from "./utils.js";
greet();
</script>
This is valid because module scripts have a dependency resolution phase that can happen asynchronously. The async attribute tells the browser to execute the module as soon as all its imports are resolved, without waiting for the document to finish parsing.
❌ Invalid: async with type="text/javascript" (not a module)
<script async type="text/javascript">
console.log("This is still invalid.");
</script>
Even though type is specified, only type="module" satisfies the requirement. The value "text/javascript" is the default classic script type and does not make async valid on an inline script.
The http-equiv attribute on a <meta> element simulates an HTTP response header, allowing you to define document-level metadata that would otherwise require server configuration. Because this metadata applies to the entire document and must be processed before the page content is rendered, the HTML specification requires that <meta http-equiv> elements appear within the <head> element. Placing them in the <body> is invalid and may cause browsers to ignore them entirely, leading to unexpected behavior like incorrect character encoding, broken content security policies, or missing refresh directives.
This error commonly occurs when:
- A <meta http-equiv> tag is accidentally placed inside <body>.
- Content is copy-pasted from one document into the body of another, bringing along <meta> tags.
- A templating system or CMS injects <meta> tags in the wrong location.
Common http-equiv values
The http-equiv attribute supports several standard values:
- content-type — Declares the document’s MIME type and character encoding. In HTML5, the shorthand <meta charset="UTF-8"> is preferred instead.
- refresh — Instructs the browser to reload the page or redirect after a specified number of seconds. Note that automatic refreshing is discouraged for accessibility reasons: it can disorient users, move focus back to the top of the page, and disrupt assistive technology. Avoid it unless absolutely necessary.
- content-security-policy — Defines a Content Security Policy for the document, helping prevent cross-site scripting (XSS) and other code injection attacks.
- default-style — Specifies the preferred stylesheet from a set of alternative stylesheets.
How to fix it
Find every <meta http-equiv> element in your document and ensure it is placed inside the <head> element, before any content in <body>. If the tag is duplicated or unnecessary, remove it.
Examples
❌ Incorrect: http-equiv inside <body>
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<p>Hello, world!</p>
</body>
</html>
The <meta http-equiv> tag is inside <body>, which triggers the validation error.
✅ Correct: http-equiv inside <head>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
✅ Correct: using the HTML5 charset shorthand
In HTML5, you can replace <meta http-equiv="content-type" content="text/html; charset=UTF-8"> with the simpler charset attribute:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Page</title>
</head>
<body>
<p>Hello, world!</p>
</body>
</html>
✅ Correct: Content Security Policy in <head>
<head>
<meta charset="UTF-8">
<meta http-equiv="content-security-policy" content="default-src 'self'">
<title>Secure Page</title>
</head>
The content-security-policy value is particularly placement-sensitive — browsers will ignore it if it appears outside <head>, leaving your page without the intended security protections.
The autocomplete attribute tells the browser whether it can assist the user in filling out a form field, and if so, what type of data is expected. The generic values "on" and "off" control whether the browser should offer autofill suggestions to the user. Since type="hidden" inputs are never displayed and never receive direct user input, these values don’t apply — there’s no user interaction to assist with.
According to the HTML specification, hidden inputs can have an autocomplete attribute, but only with specific named autofill detail tokens (like "transaction-id" or "cc-number"). These tokens serve a programmatic purpose by providing hints about the semantic meaning of the hidden value, which can be useful for form processing. The generic "on" and "off" values, however, are explicitly disallowed because they only relate to the user-facing autofill behavior.
This validation error matters for standards compliance and can indicate a logical mistake in your markup. If you added autocomplete="off" to a hidden input hoping to prevent the browser from caching or modifying the value, it won’t have that effect. Hidden input values are controlled entirely by the server or by JavaScript, not by browser autofill.
How to fix it
- Remove the autocomplete attribute if it’s not needed — this is the most common fix.
- Use a specific autofill token if you need to convey semantic meaning about the hidden value (e.g., autocomplete="transaction-id").
- Reconsider the input type — if the field genuinely needs autofill behavior controlled, it probably shouldn’t be type="hidden".
Examples
Incorrect: using autocomplete="off" on a hidden input
<form action="/submit" method="post">
<input type="hidden" name="token" value="abc123" autocomplete="off">
<button type="submit">Submit</button>
</form>
Incorrect: using autocomplete="on" on a hidden input
<form action="/submit" method="post">
<input type="hidden" name="session-id" value="xyz789" autocomplete="on">
<button type="submit">Submit</button>
</form>
Correct: removing the autocomplete attribute
<form action="/submit" method="post">
<input type="hidden" name="token" value="abc123">
<button type="submit">Submit</button>
</form>
Correct: using a specific autofill token
If the hidden input carries a value with a well-defined autofill semantic, you can use a named token:
<form action="/checkout" method="post">
<input type="hidden" name="txn" value="TXN-001" autocomplete="transaction-id">
<button type="submit">Complete Purchase</button>
</form>
This is valid because "transaction-id" is a specific autofill detail token recognized by the specification, unlike the generic "on" or "off" values.
HTML5 uses a specific serialization format that is distinct from XML. While XML allows you to declare arbitrary namespace prefixes using xmlns:prefix attributes, the HTML parser does not recognize or process these declarations. The HTML specification only supports a small set of predefined namespaces — the default HTML namespace, SVG (http://www.w3.org/2000/svg), MathML (http://www.w3.org/1998/Math/MathML), XLink, XML, and XMLNS — and these are handled implicitly through the appropriate embedding elements (<svg>, <math>), not through explicit xmlns: declarations.
The phrase “not serializable as XML 1.0” in the validator message means that this attribute cannot be round-tripped between the HTML parser and an XML serializer. The HTML parser treats xmlns:w as an opaque attribute name containing a colon, rather than as a namespace declaration. If you were to serialize this DOM back to XML, the result would be invalid because xmlns:w would be interpreted as a namespace declaration for a prefix that may conflict with actual element usage.
This issue almost always originates from content exported or pasted from Microsoft Word, Excel, or other Office applications. These tools generate markup laden with proprietary namespace declarations like xmlns:w, xmlns:o (Office), xmlns:v (VML), and xmlns:x (Excel). Along with these declarations come Office-specific elements and attributes (e.g., <w:WordDocument>, <o:OfficeDocumentSettings>) that have no meaning in a web browser and bloat your HTML.
Beyond being a validation error, leaving this markup in place creates several problems. It adds significant unnecessary weight to your pages, makes the source code harder to read and maintain, and signals that the HTML was auto-generated without cleanup — which can affect long-term maintainability. Browsers silently ignore these attributes and elements, so they provide no functional benefit.
How to fix it
- Remove all xmlns: prefixed attributes from your HTML elements, including xmlns:w, xmlns:o, xmlns:v, xmlns:x, and any others.
- Remove any Office-specific elements that use those namespace prefixes, such as <o:p>, <w:WordDocument>, or <v:shapetype>. These elements are meaningless in HTML5.
- Clean up associated conditional comments like <!--[if gte mso 9]> that often wrap Office-specific blocks.
- Consider using a paste-cleanup tool if you regularly paste content from Word into your HTML. Many CMS platforms and text editors offer “paste as plain text” or “clean HTML” options.
If you genuinely need to work with Office Open XML namespaces, use an appropriate XML-based format (XHTML served as application/xhtml+xml, or OOXML documents) rather than standard HTML5.
Examples
Incorrect — Office namespace declarations in HTML
This markup is typical of content saved or exported from Microsoft Word:
<html xmlns:w="urn:schemas-microsoft-com:office:word"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<title>Report</title>
<!--[if gte mso 9]>
<w:WordDocument>
<w:View>Normal</w:View>
</w:WordDocument>
<![endif]-->
</head>
<body>
<p class="MsoNormal">Hello world<o:p></o:p></p>
</body>
</html>
Correct — Clean HTML without Office markup
<!DOCTYPE html>
<html lang="en">
<head>
<title>Report</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>
Incorrect — Single namespace on the <html> element
Even a single custom namespace declaration triggers the error:
<html xmlns:w="urn:schemas-microsoft-com:office:word">
<head>
<title>My Page</title>
</head>
<body>
<p>Content here.</p>
</body>
</html>
Correct — Remove the attribute entirely
<!DOCTYPE html>
<html lang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>Content here.</p>
</body>
</html>
Note that the default xmlns="http://www.w3.org/1999/xhtml" attribute on the <html> element is permitted (though unnecessary in HTML5), but any prefixed namespace declaration like xmlns:w is not.
When graphic design tools like Affinity Designer (formerly Serif) export SVG files, they often embed custom namespace declarations such as xmlns:serif="http://www.serif.com/". These namespaces allow the editor to store its own metadata — like layer names, grouping information, or application-specific settings — inside the SVG file. While this metadata is useful if you re-open the file in the original editor, it has no meaning in a web browser.
The HTML5 specification defines a specific set of namespace attributes that are allowed in SVG elements (such as xmlns, xmlns:xlink, and xmlns:xml). Any namespace prefix not in this predefined list — like xmlns:serif, xmlns:inkscape, or xmlns:sodipodi — triggers this validation error because the HTML parser cannot serialize these attributes back into well-formed XML 1.0. This isn’t just a theoretical concern: non-serializable attributes can cause issues when the DOM is manipulated via JavaScript or when the markup is processed by XML-based tools.
Beyond the xmlns:serif declaration itself, you’ll likely find attributes in the SVG that use this namespace prefix, such as serif:id="layer1". These should also be removed since they reference a namespace the browser doesn’t understand.
How to Fix It
- Remove the xmlns:serif attribute from the <svg> element.
- Remove any attributes prefixed with serif: (e.g., serif:id) from child elements within the SVG.
- If you re-export the SVG, check your editor’s export settings — some tools offer a “clean” or “optimized” export option that strips proprietary metadata.
- Consider using an SVG optimization tool like SVGO to automatically clean up unnecessary attributes.
Examples
❌ Invalid: SVG with xmlns:serif attribute
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:serif="http://www.serif.com/"
viewBox="0 0 100 100"
width="100"
height="100">
<g serif:id="Layer 1">
<circle cx="50" cy="50" r="40" fill="blue" />
</g>
</svg>
This triggers the error because xmlns:serif is not a recognized namespace in HTML5, and serif:id references that unsupported namespace.
✅ Valid: SVG with proprietary attributes removed
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
width="100"
height="100">
<g>
<circle cx="50" cy="50" r="40" fill="blue" />
</g>
</svg>
Both xmlns:serif and serif:id have been removed. The SVG renders identically in the browser since those attributes were only meaningful to the editing application.
Handling Multiple Proprietary Namespaces
Exported SVGs sometimes contain several non-standard namespaces at once. Remove all of them:
<!-- ❌ Invalid: multiple proprietary namespaces -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:serif="http://www.serif.com/"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
viewBox="0 0 200 200">
<rect x="10" y="10" width="180" height="180" fill="red"
serif:id="background"
inkscape:label="bg-rect" />
</svg>
<!-- ✅ Valid: all proprietary namespaces and prefixed attributes removed -->
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 200">
<rect x="10" y="10" width="180" height="180" fill="red" />
</svg>
If you frequently work with SVGs from design tools, integrating an SVG optimizer into your build process can save time and ensure these non-standard attributes never reach production.
The grid-template-rows property defines the size of each row in a CSS grid layout. The W3C validator checks that every value in the declaration conforms to the CSS Grid specification. When you see this error, the validator has encountered something it cannot parse as a valid track size.
Common causes of this error include:
- Typos or invalid units — writing 100 px (with a space), 100pixels, or 1 fr instead of 1fr.
- Using values from other properties — for example, flex, inline, or space-between are not valid row track sizes.
- Incorrect function syntax — missing commas in repeat(), providing wrong arguments to minmax(), or using unsupported functions.
- Missing units — writing a bare number like 100 instead of 100px (zero is the only number that doesn’t require a unit).
- Using newer syntax not yet recognized — some cutting-edge features like subgrid or the masonry value may trigger validation warnings depending on the validator’s supported spec level.
The grid-template-rows property accepts these valid value types:
- Length values: 100px, 5em, 10rem, 20vh
- Percentages: 50%, 33.3%
- Flexible lengths: 1fr, 2fr
- Keywords: auto, min-content, max-content, none
- Functions: repeat(), minmax(), fit-content()
- Named lines: [row-start] 100px [row-end]
This matters for standards compliance and forward compatibility. While browsers may be lenient and ignore invalid values, relying on that behavior can lead to layouts that silently break. Valid CSS ensures your grid behaves predictably across all browsers.
Examples
Incorrect — invalid values
<style>
/* ERROR: "full" is not a valid track size */
.grid-a {
display: grid;
grid-template-rows: full auto;
}
/* ERROR: space between number and unit */
.grid-b {
display: grid;
grid-template-rows: 100 px 200 px;
}
/* ERROR: bare number without a unit */
.grid-c {
display: grid;
grid-template-rows: 100 200;
}
/* ERROR: missing comma in repeat() */
.grid-d {
display: grid;
grid-template-rows: repeat(3 1fr);
}
</style>
Correct — valid track sizes
<style>
/* Fixed pixel heights */
.grid-a {
display: grid;
grid-template-rows: 100px auto;
}
/* Flexible units */
.grid-b {
display: grid;
grid-template-rows: 1fr 2fr;
}
/* Repeat function with correct syntax */
.grid-c {
display: grid;
grid-template-rows: repeat(3, 1fr);
}
/* Minmax with auto */
.grid-d {
display: grid;
grid-template-rows: minmax(100px, 1fr) auto;
}
</style>
Full working example
<!DOCTYPE html>
<html lang="en">
<head>
<title>Grid template rows example</title>
<style>
.grid-container {
display: grid;
grid-template-rows: 80px minmax(150px, 1fr) auto;
gap: 8px;
height: 400px;
}
.grid-container > div {
background: #e0e0e0;
padding: 16px;
}
</style>
</head>
<body>
<div class="grid-container">
<div>Row 1 — fixed 80px</div>
<div>Row 2 — between 150px and 1fr</div>
<div>Row 3 — auto-sized to content</div>
</div>
</body>
</html>
Using fit-content() and named lines
<style>
.grid {
display: grid;
grid-template-rows: [header] fit-content(100px) [main] 1fr [footer] auto;
}
</style>
If your value looks correct but the validator still flags it, check whether you’re using a very new CSS feature like subgrid or masonry. These may not yet be recognized by the validator even if some browsers support them. In that case, the warning can be acknowledged while keeping the value intentionally.
The <area> element defines a clickable region within an image map (<map>). The coords attribute works together with the shape attribute to describe the geometry of that region. When the coordinates don’t conform to the rules for the given shape, the browser may ignore the area entirely or interpret it unpredictably, making the clickable region inaccessible to users.
Each shape type has strict requirements:
- Rectangle (shape="rect"): Requires exactly four integers in the format x1,y1,x2,y2, where x1,y1 is the top-left corner and x2,y2 is the bottom-right corner. Because 0,0 is the top-left of the image, x1 must be less than x2 and y1 must be less than y2.
- Circle (shape="circle"): Requires exactly three integers in the format x,y,r, where x,y is the center of the circle and r is the radius. The radius must be a positive integer, and the first coordinate (the x-center) must be less than the third value (the radius) is not required—but the validator message mentions this constraint to flag cases where values appear swapped or malformed.
- Polygon (shape="poly"): Requires at least six integers (three x,y coordinate pairs), forming a polygon with at least three vertices (a triangle). The format is x1,y1,x2,y2,...,xn,yn, and the number of integers must be even since they represent pairs.
Getting these formats wrong is a standards compliance issue. Assistive technologies such as screen readers rely on valid <area> definitions to convey interactive regions to users. Invalid coordinates can also cause the clickable area to silently fail in some browsers.
Examples
Invalid: Rectangle with swapped coordinates
The top-left corner values are larger than the bottom-right corner values:
<map name="nav">
<area shape="rect" coords="200,150,50,10" href="/home" alt="Home">
</map>
Fixed: Rectangle with correct coordinate order
<map name="nav">
<area shape="rect" coords="50,10,200,150" href="/home" alt="Home">
</map>
Invalid: Circle with wrong number of values
Four values are provided instead of the required three:
<map name="nav">
<area shape="circle" coords="100,75,50,25" href="/info" alt="Info">
</map>
Fixed: Circle with three values
<map name="nav">
<area shape="circle" coords="100,75,50" href="/info" alt="Info">
</map>
Invalid: Polygon with too few coordinates
Only four integers (two coordinate pairs) are provided, but a polygon needs at least three pairs:
<map name="nav">
<area shape="poly" coords="10,20,30,40" href="/about" alt="About">
</map>
Fixed: Polygon with at least three coordinate pairs
<map name="nav">
<area shape="poly" coords="10,20,30,40,20,60" href="/about" alt="About">
</map>
Invalid: Non-integer or malformed values
Decimal numbers and spaces in the wrong places will also trigger this error:
<map name="nav">
<area shape="rect" coords="10.5, 20, 100, 200" href="/page" alt="Page">
</map>
Fixed: Using only comma-separated integers
<map name="nav">
<area shape="rect" coords="10,20,100,200" href="/page" alt="Page">
</map>
Complete valid image map example
<img src="floorplan.png" alt="Office floor plan" usemap="#office">
<map name="office">
<area shape="rect" coords="0,0,150,100" href="/lobby" alt="Lobby">
<area shape="circle" coords="200,150,40" href="/meeting-room" alt="Meeting room">
<area shape="poly" coords="300,50,400,50,400,150,350,200,300,150" href="/lounge" alt="Lounge">
</map>
When debugging coordinate issues, double-check that the shape attribute matches the number of coordinates you’ve provided, that all values are non-negative integers separated by commas with no extra spaces, and that rectangle corners are specified in the correct top-left to bottom-right order.
The <link> element defines a relationship between the current document and an external resource — most commonly stylesheets, icons, preloaded assets, or canonical URLs. According to the HTML specification, the element must include at least one of the href or imagesrcset attributes so the browser knows what resource is being linked. A <link> element without either attribute is essentially an empty declaration: it tells the browser about a relationship type (via rel) but provides no actual resource to fetch or reference.
This validation error commonly occurs in a few scenarios:
- Templating or CMS issues: A dynamic template generates a <link> tag but the URL variable is empty or undefined, resulting in a bare element with no href.
- Incomplete code: A developer adds a <link> with a rel attribute intending to fill in the href later but forgets to do so.
- Copy-paste mistakes: Attributes are accidentally removed during editing or refactoring.
Fixing this is important for several reasons. Browsers may ignore the element entirely or behave unpredictably when encountering a <link> with no resource URL. Unnecessary elements without purpose add bloat to the document and can confuse other developers reading the code. Additionally, tools that parse HTML — such as search engine crawlers and assistive technologies — rely on well-formed markup to correctly understand document relationships.
To resolve the issue, add an href attribute pointing to the target resource, use an imagesrcset attribute when providing responsive image sources, or include both when appropriate.
Examples
Invalid: missing both href and imagesrcset
This <link> declares a stylesheet relationship but doesn’t specify where the stylesheet is located:
<link rel="stylesheet">
This preload link has an as attribute but no resource to fetch:
<link rel="preload" as="image" type="image/png">
Fixed: using href
The most common fix is adding an href attribute with a valid URL:
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="favicon.ico">
<link rel="canonical" href="https://example.com/page">
Fixed: using imagesrcset
For responsive image preloading, the imagesrcset attribute specifies multiple image sources at different resolutions. This is valid without href:
<link rel="preload" as="image" type="image/png" imagesrcset="icon-1x.png 1x, icon-2x.png 2x">
Fixed: using both href and imagesrcset
You can combine both attributes. The href serves as a fallback resource while imagesrcset provides responsive alternatives:
<link rel="preload" as="image" href="icon-1x.png" imagesrcset="icon-1x.png 1x, icon-2x.png 2x">
Handling dynamic templates
If your <link> elements are generated dynamically, make sure the element is only rendered when a valid URL is available. For example, in a template, wrap the output in a conditional check rather than outputting an empty <link>:
<!-- Bad: outputs a link even when the URL is empty -->
<link rel="stylesheet" href="">
<!-- Good: only include the element when there's a real URL -->
<link rel="stylesheet" href="styles.css">
Note that href="" resolves to the current page URL and is technically valid syntax (it won’t trigger this specific error), but it’s almost certainly not what you intend. Always ensure the href value points to the correct resource.
The HTML specification requires the src attribute of an <img> element to be a valid, non-empty URL. When you set src="", the browser has no resource to fetch, but many browsers will still attempt to make a request — often resolving the empty string relative to the current page URL. This means the browser may re-request the current HTML document as if it were an image, wasting bandwidth and potentially causing unexpected server-side behavior.
Beyond the technical waste, an empty src is problematic for accessibility. Screen readers rely on the <img> element to convey meaningful content. An image with no source provides no value and can confuse assistive technology users. Search engine crawlers may also flag this as a broken resource, negatively affecting SEO.
This issue commonly arises in a few scenarios:
- Placeholder images — developers leave src empty intending to set it later via JavaScript.
- Template rendering — a server-side template or frontend framework outputs an <img> tag before the image URL is available.
- Lazy loading implementations — the real source is stored in a data-src attribute while src is left empty.
How to fix it
The simplest fix is to provide a valid image URL in the src attribute. If the image source isn’t available yet, consider these alternatives:
- Don’t render the <img> element at all until a valid source is available.
- Use a small placeholder image (such as a transparent 1×1 pixel GIF or a lightweight SVG) as a temporary src.
- Use native lazy loading with loading="lazy" and a real src, letting the browser handle deferred loading instead of relying on an empty src.
Examples
❌ Bad: empty src attribute
<img src="" alt="Profile photo">
This triggers the validation error because the src value is an empty string.
❌ Bad: src with only whitespace
<img src=" " alt="Profile photo">
Whitespace-only values are also considered invalid and will produce a similar error.
✅ Good: valid image path
<img src="photo.jpg" alt="Profile photo">
✅ Good: placeholder image for lazy loading
If you’re implementing lazy loading and need a lightweight placeholder, use a small inline data URI or a real placeholder file:
<img
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-src="photo.jpg"
alt="Profile photo">
✅ Good: native lazy loading with a real src
Modern browsers support the loading attribute, eliminating the need for an empty src workaround:
<img src="photo.jpg" alt="Profile photo" loading="lazy">
✅ Good: conditionally render the element
If the image URL might not be available, avoid rendering the <img> tag entirely. For example, in a template:
<!-- Only include the img element when a source exists -->
<img src="photo.jpg" alt="Profile photo">
In frameworks like React, Vue, or server-side templating engines, use conditional logic to skip the <img> element when the URL is empty rather than outputting a tag with an empty src.
The <source> element is used inside <video>, <audio>, and <picture> elements to specify one or more media resources for the browser to choose from. Its src attribute is defined as a valid non-empty URL, meaning it must resolve to an actual file path or web address. An empty string does not satisfy this requirement and violates the HTML specification.
Why this is a problem
Standards compliance: The WHATWG HTML living standard explicitly requires the src attribute on <source> to be a non-empty, valid URL. An empty value makes the document invalid HTML.
Unexpected browser behavior: When a browser encounters an empty src, it may attempt to resolve it relative to the current page URL. This can trigger an unnecessary HTTP request back to the current page, resulting in wasted bandwidth, unexpected server load, or confusing errors in your network logs.
Broken media playback: An empty src means the browser has no media file to load. If the <source> element is the only one provided, the media element will fail to play entirely — often without a clear indication to the user of what went wrong.
Accessibility concerns: Screen readers and assistive technologies rely on well-formed HTML. Invalid markup can lead to unpredictable behavior or missed content announcements for users who depend on these tools.
How to fix it
- Provide a valid URL — Set the src attribute to the correct path or URL of your media file.
- Remove the element — If the media source is not yet available or is being set dynamically via JavaScript, remove the <source> element from the HTML entirely and add it later through script when the URL is known.
- Check for template or CMS issues — This error often appears when a CMS or templating engine outputs a <source> tag with an empty variable. Ensure your template conditionally renders the element only when a valid URL exists.
Examples
Incorrect: empty src on <source> in a video
<video controls>
<source src="" type="video/mp4">
Your browser does not support the video tag.
</video>
Correct: valid URL in src
<video controls>
<source src="movie.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
Incorrect: empty src on <source> in a picture element
<picture>
<source src="" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
Note that <source> inside <picture> uses srcset, not src. This example is doubly wrong — the attribute is both empty and incorrect. Here is the fix:
Correct: using srcset with a valid URL in a picture element
<picture>
<source srcset="photo.webp" type="image/webp">
<img src="photo.jpg" alt="A sunset over the ocean">
</picture>
Incorrect: multiple sources with one empty src
<audio controls>
<source src="song.ogg" type="audio/ogg">
<source src="" type="audio/mpeg">
</audio>
Correct: remove the source if no file is available
<audio controls>
<source src="song.ogg" type="audio/ogg">
<source src="song.mp3" type="audio/mpeg">
</audio>
Correct: conditionally add sources with JavaScript
If the URL is determined at runtime, avoid placing an empty <source> in your markup. Instead, add it dynamically:
<video id="player" controls>
Your browser does not support the video tag.
</video>
<script>
const videoUrl = getVideoUrl(); // your logic here
if (videoUrl) {
const source = document.createElement("source");
source.src = videoUrl;
source.type = "video/mp4";
document.getElementById("player").appendChild(source);
}
</script>
This approach keeps your HTML valid at all times and only inserts a <source> element when a real URL is available.
Ready to validate your sites?
Start your free trial today.