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 HTML specification defines maxlength as accepting only a valid non-negative integer — a string of one or more ASCII digits representing a number greater than or equal to zero. An empty string does not satisfy this requirement, so the W3C validator flags it as an error. This commonly happens when a value is dynamically generated by a CMS, template engine, or JavaScript framework and the value ends up blank, or when a developer adds the attribute as a placeholder intending to fill it in later.
Why this matters
While most browsers silently ignore an empty maxlength and impose no character limit, relying on this behavior is problematic for several reasons:
- Standards compliance: Invalid HTML can lead to unpredictable behavior across different browsers and versions. What works today may not work tomorrow.
- Accessibility: Assistive technologies may read or interpret the
maxlengthattribute to communicate input constraints to users. An empty value could cause confusing or incorrect announcements. - Maintainability: An empty
maxlengthis ambiguous — it's unclear whether the developer intended no limit, forgot to set a value, or a bug caused the value to be missing.
How to fix it
You have two options:
- Set a valid non-negative integer: Provide a concrete number that represents the maximum number of characters the user can enter, such as
maxlength="100". - Remove the attribute: If you don't need to enforce a character limit, simply omit
maxlengthaltogether. There is no need to include it with an empty value.
Valid values for maxlength include "0", "1", "255", or any other non-negative whole number. The following are not valid: empty strings (""), negative numbers ("-1"), decimal numbers ("10.5"), or non-numeric strings ("none").
Where maxlength applies
The maxlength attribute is meaningful on text-entry input types: text, search, url, tel, email, password, and also on the textarea element. For non-text input types like number, date, range, or checkbox, the attribute has no effect and should not be used.
Examples
❌ Incorrect: empty string triggers the validation error
<inputtype="text"name="username"maxlength="">
❌ Incorrect: other invalid values
<inputtype="text"name="username"maxlength="-1">
<inputtype="email"name="email"maxlength="none">
<inputtype="text"name="bio"maxlength="10.5">
✅ Correct: explicit maximum length
<inputtype="text"name="username"maxlength="30">
✅ Correct: omit the attribute when no limit is needed
<inputtype="text"name="comment">
✅ Correct: maxlength on a textarea
<textareaname="bio"maxlength="500"></textarea>
✅ Correct: dynamic value with a fallback
If your maxlength value comes from a template or CMS, make sure you either output a valid number or omit the attribute entirely. For example, in a templating language, use conditional logic:
<!-- Only render maxlength if the value is set -->
<inputtype="text"name="username"maxlength="100">
Rather than rendering an empty attribute like maxlength="", ensure your template skips the attribute when no value is configured.
The min attribute defines the minimum acceptable value for form input types such as number, range, date, time, datetime-local, week, and month. When the browser or the W3C validator encounters min="", it attempts to parse the empty string as a floating point number and fails because the empty string is not a valid representation of any number according to the HTML specification's rules for parsing floating point numbers.
This issue commonly arises when templating engines or server-side code dynamically set the min attribute but output an empty value when no minimum is configured, or when developers add the attribute as a placeholder intending to fill it in later.
Why this matters
- Standards compliance: The HTML specification explicitly requires the
minattribute's value to be a valid floating point number (for numeric types) or a valid date/time string (for date/time types). An empty string satisfies neither requirement. - Unpredictable browser behavior: When browsers encounter an invalid
minvalue, they typically ignore the attribute entirely. This means your intended constraint silently disappears, potentially allowing users to submit out-of-range values. - Accessibility concerns: Assistive technologies may rely on
minandmaxto communicate valid input ranges to users. An invalid value can lead to confusing or missing guidance for screen reader users. - Form validation issues: Built-in browser validation using the Constraint Validation API depends on valid
minvalues. An empty string can cause the browser's native validation to behave inconsistently across different browsers.
How to fix it
You have two straightforward options:
- Provide a valid value: Set
minto the actual minimum number or date/time string you want to enforce. - Remove the attribute: If no minimum constraint is needed, simply omit the
minattribute. The input will then accept any value within its type's natural range.
If your min value comes from dynamic server-side or JavaScript logic, make sure the attribute is only rendered when a valid value is available, rather than outputting an empty string as a fallback.
Examples
❌ Invalid: empty string for min
<inputtype="number"min=""max="10">
The empty string "" is not a valid floating point number, so this triggers the validation error.
✅ Fixed: provide a valid number
<inputtype="number"min="0"max="10">
✅ Fixed: remove min if no minimum is needed
<inputtype="number"max="10">
❌ Invalid: empty min on a range input
<inputtype="range"min=""max="100"step="5">
✅ Fixed: valid min on a range input
<inputtype="range"min="0"max="100"step="5">
❌ Invalid: empty min on a date input
<inputtype="date"min=""max="2025-12-31">
For date inputs, min must be a valid date string in YYYY-MM-DD format — an empty string is equally invalid here.
✅ Fixed: valid min on a date input
<inputtype="date"min="2025-01-01"max="2025-12-31">
Handling dynamic values in templates
If you're using a templating language and the minimum value might not always exist, conditionally render the attribute rather than outputting an empty value. For example, in a generic template pseudocode:
<!-- Instead of always outputting the attribute: -->
<inputtype="number"min=""max="10">
<!-- Only include it when a value is available: -->
<inputtype="number"min="5"max="10">
In practice, use your templating engine's conditional logic (e.g., {% if min_value %}min="{{ min_value }}"{% endif %} in Jinja2, or similar constructs) to ensure min is only present when it holds a valid value.
The HTML specification defines a strict content model for the <ul> (unordered list) element: its permitted content is zero or more <li> elements, optionally intermixed with <script> and <template> elements. A <div> is not among these allowed children, so placing one directly inside a <ul> produces a validation error.
This issue commonly arises when developers try to group or wrap list items for styling or layout purposes. For example, you might want to add a container around certain <li> elements for flexbox or grid alignment, or you might be using a templating system that injects wrapper <div> elements into your list markup.
Why this matters
- Browser parsing inconsistencies: When browsers encounter invalid nesting, they attempt to fix the DOM structure automatically, but different browsers may handle it differently. This can lead to unexpected rendering where list items appear outside their intended container or styles break unpredictably.
- Accessibility: Screen readers and assistive technologies rely on correct semantic structure to convey list relationships to users. A
<div>breaking the<ul>→<li>relationship can cause assistive tools to misinterpret or skip list content entirely. - Standards compliance: Invalid HTML can cause cascading parser errors — note that the validator message says "Suppressing further errors from this subtree," meaning additional issues within that structure may be hidden from you.
How to fix it
- Move the
<div>inside the<li>: If you need a wrapper for styling, place it inside the list item rather than around it. - Remove the
<div>entirely: If it serves no purpose, simply remove it and let the<li>elements sit directly inside the<ul>. - Use CSS on the
<li>elements: In many cases, you can apply the styles you need directly to the<li>elements without an extra wrapper. - Use
role="list"on a<div>parent: If your layout truly requires<div>wrappers, consider restructuring with ARIA roles, though native semantic elements are always preferred.
Examples
❌ Incorrect: <div> as a direct child of <ul>
<ul>
<div>
<li>Apples</li>
<li>Bananas</li>
</div>
<div>
<li>Carrots</li>
<li>Dates</li>
</div>
</ul>
✅ Correct: Move the <div> inside each <li>
<ul>
<li><div>Apples</div></li>
<li><div>Bananas</div></li>
<li><div>Carrots</div></li>
<li><div>Dates</div></li>
</ul>
✅ Correct: Remove the <div> entirely
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Carrots</li>
<li>Dates</li>
</ul>
❌ Incorrect: Using a <div> as a styling wrapper around list items
<ulclass="product-list">
<divclass="row">
<li>Product A</li>
<li>Product B</li>
<li>Product C</li>
</div>
</ul>
✅ Correct: Apply layout styles directly to the <ul> and <li> elements
<ulclass="product-list row">
<li>Product A</li>
<li>Product B</li>
<li>Product C</li>
</ul>
❌ Incorrect: Template or component wrapper inserting a <div>
This often happens in frameworks where a component renders a wrapping <div>:
<ul>
<divclass="list-item-wrapper">
<li>Item 1</li>
</div>
<divclass="list-item-wrapper">
<li>Item 2</li>
</div>
</ul>
✅ Correct: Move the wrapper class to the <li> itself
<ul>
<liclass="list-item-wrapper">Item 1</li>
<liclass="list-item-wrapper">Item 2</li>
</ul>
The same rule applies to <ol> (ordered list) elements — they share the same content model restriction. Always ensure that <li> is the direct child of <ul> or <ol>, and place any additional wrapper elements inside the <li> rather than around it.
The maxlength attribute controls the maximum number of characters a user can type into a <textarea>. According to the HTML specification, its value must be a valid non-negative integer — that is, a string of one or more ASCII digits like 0, 100, or 5000. An empty string (""), whitespace, negative numbers, or non-numeric values are all invalid. When the browser encounters an invalid maxlength value, its behavior becomes unpredictable — some browsers may ignore the attribute, while others may silently enforce no limit, leading to inconsistent form behavior across platforms.
This issue frequently arises when a server-side template or JavaScript framework conditionally outputs the maxlength attribute but produces an empty value when no limit is configured. For example, a template like maxlength="{{ maxChars }}" will render maxlength="" if the maxChars variable is empty or undefined. The fix is to ensure the attribute is omitted entirely when no value is available, rather than rendering it with an empty string.
Omitting maxlength allows unlimited input. Setting it to 0 is technically valid but prevents the user from entering any characters at all, which is rarely useful. Choose a value that makes sense for your use case, such as the corresponding database column's character limit.
Why this matters
- Standards compliance: The HTML specification explicitly requires a valid non-negative integer. An empty string violates this rule and produces a validation error.
- Consistent behavior: Browsers handle invalid attribute values differently. A valid value ensures the character limit works reliably across all browsers.
- Accessibility: Screen readers and assistive technologies may announce the maximum character limit to users. An empty or invalid value could cause confusing announcements or be silently ignored.
- Form reliability: If your application depends on
maxlengthfor client-side input restrictions (e.g., to match a database column limit), an invalid value means the constraint isn't enforced, potentially leading to data truncation or server errors.
How to fix it
- Set a specific integer value if you need a character limit:
maxlength="200". - Remove the attribute entirely if no limit is needed. An absent
maxlengthmeans unlimited input. - Fix your templates — if you're using a server-side language or JavaScript framework, conditionally render the attribute so it's omitted when no value is provided rather than output as empty.
Examples
❌ Invalid: empty maxlength value
The empty string is not a valid non-negative integer, so this triggers the validation error.
<labelfor="msg">Message</label>
<textareaid="msg"name="message"maxlength=""></textarea>
❌ Invalid: non-numeric maxlength value
Strings, decimals, and negative numbers are also invalid.
<labelfor="bio">Bio</label>
<textareaid="bio"name="bio"maxlength="none"></textarea>
<labelfor="notes">Notes</label>
<textareaid="notes"name="notes"maxlength="-1"></textarea>
✅ Fixed: specific integer value
Set maxlength to the desired character limit.
<labelfor="msg">Message (max 200 characters)</label>
<textareaid="msg"name="message"maxlength="200"></textarea>
✅ Fixed: attribute omitted entirely
If no character limit is needed, simply remove the attribute.
<labelfor="msg">Message</label>
<textareaid="msg"name="message"></textarea>
✅ Fixed: conditional rendering in a template
If you're using a templating engine, conditionally include the attribute only when a value exists. The exact syntax depends on your framework — here's a conceptual example:
<!-- Instead of always outputting the attribute: -->
<!-- <textarea maxlength="{{ maxChars }}"></textarea> -->
<!-- Only render it when maxChars has a value: -->
<!-- {% if maxChars %}<textarea maxlength="{{ maxChars }}"></textarea>{% endif %} -->
<labelfor="feedback">Feedback</label>
<textareaid="feedback"name="feedback"maxlength="500"></textarea>
URLs follow strict syntax rules defined by RFC 3986, which does not allow literal space characters. When the W3C validator encounters a space in the src attribute of an <img> element, it reports it as an illegal character in the scheme data. While most modern browsers will attempt to handle spaces in URLs by automatically encoding them, relying on this behavior is not standards-compliant and can lead to broken images in certain contexts, such as when URLs are processed by other tools, APIs, or older browsers.
This issue commonly arises when file names contain spaces (e.g., my photo.jpg) and the path is copied directly into the HTML. It can also happen when a URL is incorrectly constructed by concatenating strings with unencoded values.
Beyond validation, unencoded spaces can cause real problems. A URL with a space may break when shared, copied, or passed through redirects. Email clients and messaging apps may truncate the URL at the space. Search engine crawlers might fail to index the resource correctly.
How to fix it
There are several ways to resolve this issue:
- Percent-encode spaces as
%20— Replace every literal space in the URL with%20. - Rename the file — Remove spaces from file names entirely, using hyphens (
-), underscores (_), or camelCase instead. - Use proper URL encoding functions — If generating URLs dynamically (e.g., in JavaScript or a server-side language), use built-in encoding functions like
encodeURI()orencodeURIComponent().
Renaming files to avoid spaces is generally the best long-term practice, as it prevents encoding issues across your entire workflow.
Examples
❌ Invalid: spaces in the src attribute
<imgsrc="images/my photo.jpg"alt="A vacation photo">
<imgsrc="/assets/blog posts/header image.png"alt="Blog header">
Both of these will trigger the validation error because the src value contains literal space characters.
✅ Fixed: spaces encoded as %20
<imgsrc="images/my%20photo.jpg"alt="A vacation photo">
<imgsrc="/assets/blog%20posts/header%20image.png"alt="Blog header">
✅ Fixed: file renamed to remove spaces
<imgsrc="images/my-photo.jpg"alt="A vacation photo">
<imgsrc="/assets/blog-posts/header-image.png"alt="Blog header">
A note on + vs %20
You may see spaces encoded as + in some contexts (e.g., query strings in form submissions using application/x-www-form-urlencoded). However, + is not a valid substitute for a space in a URL path. Always use %20 for spaces in the src attribute.
<!-- ❌ Incorrect: + does not represent a space in a URL path -->
<imgsrc="images/my+photo.jpg"alt="A vacation photo">
<!-- ✅ Correct -->
<imgsrc="images/my%20photo.jpg"alt="A vacation photo">
When you write a phone link using <a href="callto:...">, you may encounter two distinct problems at once. First, the callto: scheme is a legacy, non-standard protocol originally associated with Skype. The correct and widely supported URI scheme for telephone links is tel:, as defined by RFC 3966. Second, spaces within URI scheme data are illegal characters. URIs must not contain unencoded spaces anywhere, and telephone URIs specifically expect a compact phone number composed of digits, hyphens (-), dots (.), and an optional leading plus sign (+) for international dialing.
The W3C validator raises this error because the value provided to href violates URI syntax rules. Browsers may still attempt to handle the link, but behavior will be inconsistent — some mobile browsers may not recognize callto: at all, and spaces in the URI can cause the number to be parsed incorrectly or truncated. Using the standard tel: scheme with a properly formatted number ensures the link works reliably across devices and platforms, including mobile phones, VoIP applications, and assistive technologies.
How to fix it
- Replace
callto:withtel:— Thetel:scheme is the standard for phone number links and is supported by all modern browsers and mobile operating systems. - Remove spaces and slashes — Strip out any spaces, slashes, or parentheses from the phone number. These characters are not valid in a
tel:URI without percent-encoding, and they serve no functional purpose in the link target. - Use a leading
+for international numbers — If applicable, include the full international dialing code prefixed with+(e.g.,+1for the US,+49for Germany). This makes the link work regardless of the caller's location. - Optional visual separators — If you want visual separators within the
hreffor readability in your source code, use hyphens (-) or dots (.), which are permitted intel:URIs. However, the simplest and safest approach is digits only (plus the optional leading+).
Examples
Incorrect: callto: with spaces and slashes
This triggers the validator error because spaces and slashes are illegal in URI scheme data, and callto: is non-standard.
<ahref="callto:07142/ 12 34 5">Call us</a>
Incorrect: tel: with spaces
Even with the correct tel: scheme, spaces in the phone number are still invalid URI characters.
<ahref="tel:07142 12 34 5">Call us</a>
Correct: tel: with digits only
<ahref="tel:0714212345">Call us</a>
Correct: International number with + prefix
<ahref="tel:+490714212345">Call us</a>
Correct: Using hyphens for readability
Hyphens are valid characters in tel: URIs and can improve source code readability without affecting functionality.
<ahref="tel:+49-07142-12345">Call us</a>
Displaying a formatted number to the user
You can still show a human-friendly formatted number as the visible link text while keeping the href value clean and valid.
<ahref="tel:+490714212345">+49 (0) 7142 / 12 34 5</a>
This approach gives you the best of both worlds: the link text is easy for users to read, and the href value is a valid, standards-compliant tel: URI that works reliably across all devices and passes W3C validation.
The step attribute specifies the granularity that an input's value must adhere to. It controls the increment when a user clicks the up/down spinner buttons on a number input, moves a slider on a range input, or adjusts date and time inputs. The browser also uses this value during constraint validation to determine which values are considered valid based on the stepping base (typically the min value or the input's default).
When the HTML specification defines the step attribute, it requires the value to be a valid floating-point number greater than zero. A value of "0" is explicitly invalid because a step of zero means no increment at all — it's logically meaningless. You can't step through values in increments of nothing. The W3C validator flags this as an error because "0" fails the requirement of being a positive number.
Why this matters
- Standards compliance: The WHATWG HTML living standard explicitly requires
stepto parse as a number greater than zero. A value of"0"violates this rule. - Browser behavior: While browsers may not crash on
step="0", the behavior becomes unpredictable. Spinner buttons may stop working, and form validation may not function as expected. - Accessibility: Assistive technologies rely on correct
stepvalues to communicate valid input ranges to users. An invalid step can lead to a confusing experience.
How to fix it
Choose the appropriate fix depending on your intent:
- If you want specific increments (e.g., whole numbers, cents, tenths), set
stepto the desired interval like"1","0.01", or"0.1". - If you want to allow any value with no stepping constraint, use the special keyword
step="any". This tells the browser that no stepping is implied and any floating-point value is acceptable. - If you don't need the attribute at all, simply remove it. Each input type has a default step value (e.g.,
1fortype="number").
Examples
❌ Invalid: step set to zero
<labelfor="price">Price:</label>
<inputid="price"name="price"type="number"step="0"min="0">
This triggers the validation error because "0" is not a valid positive floating-point number.
✅ Fixed: using a specific step value
If you want the input to accept values in increments of one cent:
<labelfor="price">Price:</label>
<inputid="price"name="price"type="number"step="0.01"min="0">
Valid values would include 0, 0.01, 0.02, 1.50, 99.99, and so on.
✅ Fixed: using step="any" for unrestricted precision
If you want to allow any numeric value without stepping constraints:
<labelfor="price">Price:</label>
<inputid="price"name="price"type="number"step="any"min="0">
This permits any floating-point value, such as 3.14159 or 0.007.
✅ Fixed: using a whole number step with a non-zero minimum
<labelfor="quantity">Quantity:</label>
<inputid="quantity"name="quantity"type="number"step="2"min="1.3">
With step="2" and min="1.3", valid values include 1.3, 3.3, 5.3, 7.3, and so on. The stepping base is 1.3, and each valid value is an even multiple of 2 away from it.
✅ Fixed: removing the attribute entirely
If the default step behavior is sufficient, simply omit the attribute:
<labelfor="amount">Amount:</label>
<inputid="amount"name="amount"type="number"min="0">
The default step for type="number" is 1, so valid values are whole numbers (0, 1, 2, etc.).
URLs follow strict syntax rules defined by RFC 3986, which does not permit literal space characters anywhere in the URI — including path segments, query strings, and fragment identifiers. When the W3C HTML Validator encounters a space in the src attribute of a <script> element, it flags it as an illegal character because the attribute value is not a valid URL.
While most modern browsers will silently fix this by encoding the space before making the request, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires that the
srcattribute contain a valid URL. A URL with a literal space is technically malformed. - Cross-browser reliability: Not all user agents, proxies, or CDNs handle malformed URLs the same way. What works in one browser may fail in another context.
- Interoperability: Other tools that consume your HTML — such as linters, crawlers, screen readers, and build pipelines — may not be as forgiving as browsers.
- Copy-paste and linking issues: Literal spaces in URLs cause problems when users copy links or when URLs appear in plain-text contexts like emails, where the space may break the URL in two.
How to fix it
You have three options, listed from most recommended to least:
- Rename the file or directory to eliminate spaces entirely (e.g., use hyphens or underscores). This is the cleanest solution.
- Percent-encode the space as
%20in thesrcattribute value. - Use a build tool or bundler that generates references with properly encoded or space-free paths automatically.
Avoid using + as a space replacement in path segments. The + character represents a space only in application/x-www-form-urlencoded query strings, not in URL path segments.
Examples
❌ Invalid: space in the path segment
<scriptsrc="https://example.com/media assets/app.js"></script>
The space between media and assets makes this an invalid URL.
✅ Fixed: percent-encode the space
<scriptsrc="https://example.com/media%20assets/app.js"></script>
Replacing the space with %20 produces a valid, standards-compliant URL.
✅ Better: rename to avoid spaces entirely
<scriptsrc="https://example.com/media-assets/app.js"></script>
Using a hyphen (or underscore) instead of a space is the preferred approach. It keeps URLs clean, readable, and free of encoding issues.
❌ Invalid: space in a local relative path
This issue isn't limited to absolute URLs. Relative paths trigger the same error:
<scriptsrc="js/my script.js"></script>
✅ Fixed: encode or rename the local file
<scriptsrc="js/my%20script.js"></script>
Or, better yet:
<scriptsrc="js/my-script.js"></script>
Multiple spaces and other special characters
If a URL contains multiple spaces or other special characters, each one must be individually encoded. For example, { becomes %7B and } becomes %7D. A quick reference for common characters:
| Character | Encoded form |
|---|---|
| Space | %20 |
[ | %5B |
] | %5D |
{ | %7B |
} | %7D |
<!-- Invalid -->
<scriptsrc="libs/my library [v2].js"></script>
<!-- Valid -->
<scriptsrc="libs/my%20library%20%5Bv2%5D.js"></script>
<!-- Best: rename the file -->
<scriptsrc="libs/my-library-v2.js"></script>
Note that this same rule applies to the src attribute on other elements like <img>, <iframe>, <audio>, and <video>, as well as the href attribute on <a> and <link>. Whenever you reference a URL in HTML, make sure it contains no literal spaces.
The poster attribute specifies an image to display as a placeholder while the video is loading or before the user starts playback. Like all HTML attributes that accept URLs — such as src, href, and action — the value must conform to valid URI syntax as defined by RFC 3986. In this standard, a literal space character is not a legal character in any part of a URL. When the validator encounters a space in the poster attribute's value, it flags it as an illegal character in the path segment.
While most modern browsers are forgiving and will attempt to resolve URLs containing raw spaces by internally encoding them, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires valid URLs. Raw spaces violate this requirement.
- Interoperability: Not all user agents, HTTP clients, or content delivery systems handle unencoded spaces the same way. Some may truncate the URL at the first space or fail to resolve the resource entirely.
- Portability: If your HTML is consumed by tools, scrapers, or APIs that strictly parse URLs, unencoded spaces can cause silent failures.
- Consistency: Keeping URLs properly encoded prevents subtle bugs when paths are constructed dynamically in server-side or client-side code.
The fix is straightforward. You have two options:
- Percent-encode the spaces: Replace every space in the URL with
%20. This preserves the original file and folder names on the server while producing a valid URL in your HTML. - Eliminate spaces from file and folder names: Use hyphens (
-), underscores (_), or camelCase instead of spaces. This is generally considered best practice for web assets, as it avoids encoding issues across the board.
Note that this rule applies to the entire URL path, not just the filename. If any directory in the path contains a space, it must also be encoded or renamed. The same principle applies to other special characters that are reserved or disallowed in URLs, such as {, }, |, ^, and [.
Examples
Incorrect — space in the path
The folder name video images contains a space, which is illegal in a URL path segment.
<videocontrolsposter="/img/video images/snapshot.png">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Incorrect — space in the filename
The filename my poster.jpg also triggers the same error.
<videocontrolsposter="/img/my poster.jpg">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Fixed — percent-encoding the spaces
Each space is replaced with %20, producing a valid URL.
<videocontrolsposter="/img/video%20images/snapshot.png">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Fixed — removing spaces from the path
Renaming the folder to use a hyphen eliminates the need for encoding entirely.
<videocontrolsposter="/img/video-images/snapshot.png">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
Fixed — removing spaces from the filename
<videocontrolsposter="/img/my-poster.jpg">
<sourcesrc="/videos/sample.mp4"type="video/mp4">
</video>
As a general best practice, avoid spaces in all file and folder names used on the web. Use hyphens or underscores instead. If you're working with files you can't rename — such as assets from a CMS or third-party system — always percent-encode spaces as %20 in your HTML. This applies not only to poster but to every attribute that takes a URL value, including src, href, action, data, and formaction.
URLs follow strict syntax rules defined by RFC 3986. Within the path segment of a URL, only a specific set of characters is allowed: unreserved characters (letters, digits, -, ., _, ~), percent-encoded characters (like %20), and certain reserved sub-delimiters. When the W3C validator encounters a character outside this allowed set in a <link> element's href attribute, it flags the error.
Common causes of this issue include:
- Template placeholders left in the URL, such as
{{variable}}or${path}, where curly braces and dollar signs haven't been resolved or encoded. - Spaces in file paths, such as
href="styles/my file.css"instead of using%20or renaming the file. - Copy-paste errors that introduce invisible or special Unicode characters.
- Backslashes (
\) used instead of forward slashes (/), which is a common mistake on Windows systems. - Unencoded query-like characters placed in the path portion of the URL.
This matters because browsers may interpret malformed URLs inconsistently. A URL that works in one browser might fail in another. Additionally, invalid URLs can break resource loading, cause accessibility issues when assistive technologies try to process the document, and lead to unexpected behavior with proxies, CDNs, or other intermediaries that strictly parse URLs.
To fix the issue, inspect the href value reported in the error and either:
- Remove the illegal character if it was included by mistake.
- Percent-encode the character if it must be part of the URL (e.g., a space becomes
%20, a pipe|becomes%7C). - Rename the referenced file or directory to avoid special characters altogether (the simplest and most reliable approach).
Examples
Incorrect: Space in the path
<linkrel="stylesheet"href="styles/my styles.css">
The space character is not allowed in a URL path segment. The validator will flag this as an illegal character.
Fixed: Percent-encode the space
<linkrel="stylesheet"href="styles/my%20styles.css">
Better fix: Rename the file to avoid spaces
<linkrel="stylesheet"href="styles/my-styles.css">
Incorrect: Template placeholder left unresolved
<linkrel="stylesheet"href="styles/{{theme}}/main.css">
Curly braces { and } are not valid in URL path segments. This commonly happens with server-side or client-side templating syntax that wasn't processed before the HTML was served.
Fixed: Use a valid resolved path
<linkrel="stylesheet"href="styles/dark/main.css">
Incorrect: Backslash used as path separator
<linkrel="stylesheet"href="styles\main.css">
Backslashes are not valid URL characters. URLs always use forward slashes.
Fixed: Use forward slashes
<linkrel="stylesheet"href="styles/main.css">
Incorrect: Pipe character in the URL
<linkrel="stylesheet"href="styles/font|icon.css">
Fixed: Percent-encode the pipe character
<linkrel="stylesheet"href="styles/font%7Cicon.css">
Full valid document example
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="utf-8">
<title>My Webpage</title>
<linkrel="stylesheet"href="styles/main.css">
<linkrel="icon"href="images/favicon.ico">
</head>
<body>
<h1>Welcome to my webpage!</h1>
<p>Here is some content.</p>
</body>
</html>
When in doubt, run your URL through a URL encoder or validator separately to confirm all characters are legal. As a general best practice, stick to lowercase letters, digits, hyphens, and forward slashes in your file and directory names—this avoids encoding issues entirely and makes your URLs clean and predictable.
The autocomplete attribute helps browsers autofill form fields with previously saved user data. The HTML specification defines a strict set of valid values, and each one maps to a specific type of information (like a name, email address, phone number, or street address). The string "contact" by itself is not a valid autofill field name — it's a contact type token, which is a modifier meant to be combined with a field name to distinguish between different types of contact information.
The HTML spec defines two contact type tokens: "home", "work", "mobile", "fax", and "pager" (for phone-related fields), as well as the broader "shipping" and "billing" scoping tokens. The token "contact" doesn't exist as a standalone value at all. You may have confused it with a contact type prefix pattern like "home email" or "work tel", or you may have intended to use a specific field name entirely.
Getting the autocomplete value right matters for several reasons. Browsers rely on these exact tokens to offer relevant autofill suggestions. Screen readers and assistive technologies may also use this information to help users understand what data a field expects. An invalid value means the browser will likely ignore the attribute entirely, degrading the user experience — especially on mobile devices where autofill is heavily used.
To fix the issue, determine what kind of information the input field is collecting and use the appropriate autofill field name. Common valid values include "name", "email", "tel", "street-address", "postal-code", "organization", and "username". If you want to indicate that this is specifically a contact email or phone (as opposed to, say, a billing one), you don't use "contact" — instead, you can omit the modifier entirely or use a section-scoping approach.
Examples
❌ Invalid: Using "contact" as the autocomplete value
<labelfor="email">Contact Email</label>
<inputtype="email"id="email"name="email"autocomplete="contact">
The value "contact" is not a recognized autofill field name, so the browser cannot determine what to autofill.
✅ Fixed: Using a valid autofill field name
<labelfor="email">Contact Email</label>
<inputtype="email"id="email"name="email"autocomplete="email">
The value "email" is a valid autofill field name that tells the browser to suggest saved email addresses.
✅ Fixed: Using a valid combination with a section or contact type token
If you need to differentiate between types of phone numbers, you can use tokens like "home", "work", or "mobile" as prefixes:
<labelfor="work-tel">Work Phone</label>
<inputtype="tel"id="work-tel"name="work-tel"autocomplete="work tel">
<labelfor="home-email">Personal Email</label>
<inputtype="email"id="home-email"name="home-email"autocomplete="home email">
Common valid autocomplete values
Here are some frequently used valid autofill field names:
| Value | Purpose |
|---|---|
"name" | Full name |
"email" | Email address |
"tel" | Phone number |
"username" | Username |
"new-password" | New password (for registration) |
"current-password" | Existing password (for login) |
"street-address" | Street address |
"postal-code" | ZIP or postal code |
"country-name" | Country name |
"organization" | Company or organization |
"off" | Disable autofill |
For the complete list of valid values and their permitted combinations, refer to the WHATWG autofill specification.
A URL is made up of several parts: scheme, host (domain), path, query, and fragment. While some of these parts allow certain special characters (often percent-encoded), the host portion has strict rules. Domain names follow the DNS naming conventions, which only permit ASCII letters (a-z, A-Z), digits (0-9), hyphens (-), and dots (.) as label separators. Spaces are categorically forbidden.
This validation error typically occurs in two scenarios:
- A literal space appears in the domain, e.g.,
http://my domain.com. This is often a typo or a copy-paste error. - A percent-encoded space (
%20) appears in the domain, e.g.,http://my%20domain.com. While%20is valid in URL paths and query strings, it is not valid in the host portion. Percent-encoding does not make a space legal in a domain name — it still resolves to a space character, which DNS cannot handle.
Why this is a problem
- Broken links: Browsers cannot resolve a domain with spaces to an actual server. Users clicking the link will get an error or be taken nowhere.
- Accessibility: Screen readers and assistive technologies may announce the link, but users will encounter a dead end, creating a frustrating experience.
- Standards compliance: The WHATWG URL Standard explicitly forbids spaces in the host component. The W3C validator flags this to help you catch what is almost certainly a mistake.
- SEO impact: Search engine crawlers will treat the URL as invalid and will not follow or index it.
How to fix it
- Check for typos: The most common fix is to correct the domain to the actual, valid domain name you intended.
- Replace spaces with hyphens: If the intended domain genuinely has a word separator, the standard convention is to use hyphens (e.g.,
my-domain.com). - Remove spaces entirely: Sometimes spaces are accidentally introduced and simply need to be removed (e.g.,
mydomain.com). - Check the path vs. host: If the space belongs in a file path or query parameter rather than the domain, make sure it's in the correct part of the URL and properly percent-encoded there.
Examples
❌ Literal space in the domain
<ahref="http://my domain.com/page">Visit site</a>
❌ Percent-encoded space in the domain
<ahref="http://my%20domain.com/page">Visit site</a>
✅ Fixed: use a hyphen in the domain
<ahref="http://my-domain.com/page">Visit site</a>
✅ Fixed: remove the space entirely
<ahref="http://mydomain.com/page">Visit site</a>
✅ Spaces are fine in the path (percent-encoded)
Note that %20 is valid in the path portion of a URL — just not in the domain:
<ahref="http://mydomain.com/my%20page">Visit page</a>
Common mistake: space before or after the domain
Sometimes the space is hard to spot because it's at the beginning or end of the URL, or between the scheme and domain:
<!-- ❌ Trailing space in domain -->
<ahref="http://mydomain.com /page">Visit site</a>
<!-- ✅ Fixed -->
<ahref="http://mydomain.com/page">Visit site</a>
If your URLs are generated dynamically (e.g., from a CMS or database), make sure to trim whitespace from the domain portion before constructing the full URL. A quick way to catch these issues during development is to validate your HTML regularly with the W3C Markup Validation Service.
The HTML specification requires that the width and height attributes on <img> elements, when present, contain a string representing a non-negative integer — that is, a sequence of one or more ASCII digits like "0", "150", or "1920". An empty string ("") does not satisfy this requirement, so the W3C validator flags it as an error.
This issue commonly arises when:
- A CMS or templating engine outputs
width=""orheight=""because no dimension value was configured. - JavaScript dynamically sets
img.setAttribute("width", "")instead of removing the attribute. - A developer adds the attributes as placeholders intending to fill them in later but forgets to do so.
Why it matters
Providing valid width and height attributes is one of the most effective ways to prevent Cumulative Layout Shift (CLS). Browsers use these values to calculate the image's aspect ratio and reserve the correct amount of space before the image loads. When the values are empty strings, the browser cannot determine the aspect ratio, so no space is reserved — leading to layout shifts as images load in, which hurts both user experience and Core Web Vitals scores.
Beyond performance, invalid attribute values can cause unpredictable rendering behavior across browsers. Some browsers may ignore the attribute, others may interpret the empty string as 0, collapsing the image to zero pixels in that dimension. Standards-compliant HTML also improves accessibility by ensuring assistive technologies can parse the document reliably.
Examples
❌ Invalid: empty string values
<imgsrc="photo.jpg"alt="A sunset over the ocean"width=""height="">
Both width and height are set to empty strings, which is not valid.
✅ Fixed: provide actual dimensions
<imgsrc="photo.jpg"alt="A sunset over the ocean"width="800"height="600">
Replace the empty strings with the image's actual pixel dimensions. These values should reflect the image's intrinsic (natural) size. CSS can still be used to scale the image visually — the browser will use the width and height ratio to reserve the correct space.
✅ Fixed: remove the attributes entirely
<imgsrc="photo.jpg"alt="A sunset over the ocean">
If you don't know the dimensions or prefer to handle sizing purely through CSS, remove the attributes altogether. An absent attribute is valid; an empty one is not.
❌ Invalid: only one attribute is empty
<imgsrc="banner.jpg"alt="Promotional banner"width="1200"height="">
Even if only one attribute has an empty value, the validation error will be triggered for that attribute.
✅ Fixed: both attributes with valid values
<imgsrc="banner.jpg"alt="Promotional banner"width="1200"height="400">
Fixing dynamic/template-generated markup
If a template language is outputting empty attributes, use a conditional to omit them when no value is available. For example, in a template:
<!-- Instead of always outputting the attributes: -->
<imgsrc="photo.jpg"alt="Description"width=""height="">
<!-- Conditionally include them only when values exist: -->
<imgsrc="photo.jpg"alt="Description"width="800"height="600">
If you're setting dimensions via JavaScript, remove the attribute rather than setting it to an empty string:
// ❌ Don't do this
img.setAttribute("width","");
// ✅ Do this instead
img.removeAttribute("width");
// ✅ Or set a valid value
img.setAttribute("width","800");
A note on values
The width and height attributes only accept non-negative integers — whole numbers without units, decimals, or percentage signs. Values like "100px", "50%", or "3.5" are also invalid. Use plain integers like "100" or "600". If you need responsive sizing with percentages or other CSS units, apply those through CSS styles instead.
The inputmode attribute is a global attribute that can be applied to any element that is editable, including <input> elements and elements with contenteditable. It tells the browser which type of virtual keyboard to present—for example, a numeric keypad, a telephone dialpad, or a URL-optimized keyboard. This is particularly useful on mobile devices where the on-screen keyboard can be tailored to the expected input.
The W3C validator raises this as an informational warning, not an error. The inputmode attribute is part of the WHATWG HTML Living Standard and is valid HTML. However, the validator flags it because browser support, while now quite broad, has historically been inconsistent. Older versions of Safari, Firefox, and some less common browsers lacked support for certain inputmode values. When inputmode is not recognized, the browser simply ignores it and shows the default keyboard—so it degrades gracefully and won't break your page.
The valid values for inputmode are:
none— No virtual keyboard; useful when the page provides its own input interface.text— Standard text keyboard (the default).decimal— Numeric keyboard with a decimal separator, ideal for fractional numbers.numeric— Numeric keyboard without a decimal separator, ideal for PINs or zip codes.tel— Telephone keypad layout with digits 0–9,*, and#.search— A keyboard optimized for search input, which may include a "Search" or "Go" button.email— A keyboard optimized for email entry, typically including@and.prominently.url— A keyboard optimized for URL entry, typically including/and.com.
It's important to understand the difference between inputmode and the type attribute. The type attribute on <input> defines the semantics and validation behavior of the field (e.g., type="email" validates that the value looks like an email address). The inputmode attribute only affects the virtual keyboard hint and has no impact on validation or semantics. This makes inputmode especially useful when you need a specific keyboard but the field type doesn't match—for example, a numeric PIN field that should remain type="text" to avoid the spinner controls that come with type="number".
How to fix it
Since this is a warning rather than an error, no fix is strictly required. However, you should:
- Test on your target browsers and devices to confirm the virtual keyboard behaves as expected.
- Pair
inputmodewith the appropriatetypeandpatternattributes to ensure proper validation and semantics, sinceinputmodealone does not enforce any input constraints. - Accept graceful degradation — in browsers that don't support
inputmode, users will simply see the default keyboard, which is still functional.
There is no widely adopted polyfill for inputmode because it controls a browser-native UI feature (the virtual keyboard) that JavaScript cannot directly replicate. The best strategy is to treat it as a progressive enhancement.
Examples
Using inputmode for a numeric PIN field
This example triggers the validator warning. The code is valid, but the validator advises caution:
<labelfor="pin">Enter your PIN:</label>
<inputid="pin"type="text"inputmode="numeric"pattern="[0-9]*">
Here, type="text" keeps the field free of number-spinner controls, inputmode="numeric" requests a numeric keypad on mobile, and pattern="[0-9]*" provides client-side validation. This combination is the recommended approach for PIN or verification code fields.
Using inputmode for a currency amount
<labelfor="amount">Amount ($):</label>
<inputid="amount"type="text"inputmode="decimal"pattern="[0-9]*\.?[0-9]{0,2}">
The decimal value displays a numeric keyboard that includes a decimal point, which is ideal for monetary values.
Falling back to type when inputmode is unnecessary
If the semantic input type already provides the correct keyboard, you don't need inputmode at all:
<labelfor="email">Email address:</label>
<inputid="email"type="email">
<labelfor="phone">Phone number:</label>
<inputid="phone"type="tel">
<labelfor="website">Website:</label>
<inputid="website"type="url">
Using the appropriate type gives you both the optimized keyboard and built-in browser validation, making inputmode redundant in these cases.
Using inputmode on a contenteditable element
The inputmode attribute also works on non-input elements that accept user input:
<divcontenteditable="true"inputmode="numeric">
Enter a number here
</div>
This is one scenario where inputmode is especially valuable, since contenteditable elements don't have a type attribute to influence the keyboard.
The autocomplete attribute tells browsers whether and how to autofill a form field. The HTML specification defines a strict set of valid values for this attribute, known as autofill field names. These include values like "on", "off", "name", "email", "username", "new-password", "current-password", "address-line1", "postal-code", "cc-number", and many others. When you use a value that doesn't appear in this list — such as "nothanks", "nope", or "false" — the W3C validator reports it as an invalid autofill field name.
A common reason developers use made-up values is frustration with browsers ignoring autocomplete="off". Some browsers (notably Chrome) may still autofill certain fields even when autocomplete="off" is set, particularly for login-related fields. This has led to workarounds using random strings, but these are non-standard and can produce unpredictable behavior across different browsers and assistive technologies.
Why This Matters
- Standards compliance: Invalid attribute values make your HTML non-conforming, which can lead to unexpected browser behavior now or in the future.
- Accessibility: Screen readers and other assistive technologies may use the
autocompleteattribute to help users fill in forms. A recognized value like"name"or"email"gives these tools meaningful context, while a random string provides none. - Browser behavior: Browsers are designed to interpret the standard values. An unrecognized value may be treated inconsistently — some browsers might ignore it, others might treat it as
"on", and behavior could change between versions.
How to Fix It
If you want to disable autocomplete, use "off":
<inputtype="text"name="search"autocomplete="off">
If you want to help browsers autofill correctly, use the appropriate autofill field name from the specification:
<inputtype="email"name="email"autocomplete="email">
If autocomplete="off" isn't being respected by the browser (a known issue with some login fields in Chrome), consider these standards-compliant alternatives:
- Use
autocomplete="new-password"on password fields where you don't want saved passwords suggested. - Use a more specific valid token that doesn't match what the browser is trying to autofill.
- Use the
readonlyattribute and remove it on focus via JavaScript as a supplementary measure.
Examples
❌ Invalid: arbitrary string as autocomplete value
<form>
<labelfor="firstName">First name</label>
<inputtype="text"name="firstName"id="firstName"autocomplete="nothanks">
<labelfor="userEmail">Email</label>
<inputtype="email"name="userEmail"id="userEmail"autocomplete="nope">
</form>
Both "nothanks" and "nope" are not valid autofill field names and will trigger the validation error.
✅ Valid: using "off" to disable autocomplete
<form>
<labelfor="firstName">First name</label>
<inputtype="text"name="firstName"id="firstName"autocomplete="off">
<labelfor="userEmail">Email</label>
<inputtype="email"name="userEmail"id="userEmail"autocomplete="off">
</form>
✅ Valid: using proper autofill field names
<form>
<labelfor="firstName">First name</label>
<inputtype="text"name="firstName"id="firstName"autocomplete="given-name">
<labelfor="userEmail">Email</label>
<inputtype="email"name="userEmail"id="userEmail"autocomplete="email">
<labelfor="newPass">New password</label>
<inputtype="password"name="newPass"id="newPass"autocomplete="new-password">
</form>
Using descriptive autofill tokens like "given-name", "email", and "new-password" is the best approach when you want browsers and assistive technologies to understand your form fields. For a complete list of valid autofill field names, refer to the WHATWG HTML specification's autofill section.
The lang attribute on the <html> element declares the primary language of the document's content. When this attribute is missing or set incorrectly, the validator analyzes the text content and attempts to detect the language automatically. If it identifies a likely language, it produces this warning suggesting you add the appropriate lang value.
This matters for several important reasons:
- Accessibility: Screen readers rely on the
langattribute to select the correct pronunciation rules and voice profile. Without it, a screen reader might attempt to read Spanish text using English phonetics, producing unintelligible speech for the user. - Browser behavior: Browsers use the language declaration for hyphenation, quotation mark styling, spell-checking, and font selection. For example, the CSS
hyphens: autoproperty depends on thelangattribute to apply language-appropriate hyphenation rules. - Search engines: Search engines use the
langattribute as a signal to understand the language of your content, which helps serve it to the right audience in search results. - Translation tools: Automatic translation services use the declared language to determine whether (and from which language) to offer translation.
The value of the lang attribute must be a valid BCP 47 language tag. Common examples include en (English), es (Spanish), fr (French), de (German), zh (Chinese), ja (Japanese), and ar (Arabic). You can also specify regional variants like en-US, en-GB, pt-BR, or es-MX.
Examples
Missing lang attribute
This triggers the warning because the validator detects the content language but finds no lang declaration:
<!DOCTYPE html>
<html>
<head>
<metacharset="UTF-8">
<title>Mi Sitio Web</title>
</head>
<body>
<h1>Bienvenido a mi sitio web</h1>
<p>Este es un párrafo en español.</p>
</body>
</html>
Fixed with the correct lang attribute
Adding lang="es" tells browsers, screen readers, and search engines that this document is in Spanish:
<!DOCTYPE html>
<htmllang="es">
<head>
<metacharset="UTF-8">
<title>Mi Sitio Web</title>
</head>
<body>
<h1>Bienvenido a mi sitio web</h1>
<p>Este es un párrafo en español.</p>
</body>
</html>
Mismatched lang attribute
This can also trigger the warning when the lang value doesn't match the actual content. Here, the content is in French but the language is declared as English:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>Mon Site Web</title>
</head>
<body>
<h1>Bienvenue sur mon site web</h1>
<p>Ceci est un paragraphe en français.</p>
</body>
</html>
The fix is to correct the lang value to match the content:
<!DOCTYPE html>
<htmllang="fr">
<head>
<metacharset="UTF-8">
<title>Mon Site Web</title>
</head>
<body>
<h1>Bienvenue sur mon site web</h1>
<p>Ceci est un paragraphe en français.</p>
</body>
</html>
Using a regional variant
If you need to specify a regional variation, append the region subtag. For example, es-MX for Mexican Spanish or pt-BR for Brazilian Portuguese:
<!DOCTYPE html>
<htmllang="es-MX">
<head>
<metacharset="UTF-8">
<title>Mi Sitio Web</title>
</head>
<body>
<h1>Bienvenido a mi sitio web</h1>
<p>Este es un párrafo en español de México.</p>
</body>
</html>
Sections in a different language
When your document is primarily in one language but contains sections in another, set the main language on <html> and use lang on individual elements for the exceptions:
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<title>My Website</title>
</head>
<body>
<h1>Welcome to my website</h1>
<p>This site is available in multiple languages.</p>
<blockquotelang="es">
<p>Bienvenido a mi sitio web.</p>
</blockquote>
</body>
</html>
This approach ensures that assistive technologies switch pronunciation rules appropriately when they encounter the foreign-language section, while browsers and search engines still understand the primary document language.
The aria-valuenow attribute requires a valid numeric value and cannot be an empty string.
The aria-valuenow attribute is used on range-type widgets like progress bars, sliders, and spinbuttons to indicate the current value. It must be a valid floating point number — for example, 0, 50, or 3.5. An empty string ("") is not a number, so the validator rejects it.
If the current value is unknown or indeterminate, you should remove the aria-valuenow attribute entirely rather than setting it to an empty string. This is common with indeterminate progress bars where the completion percentage isn't known yet.
When present, aria-valuenow should fall between aria-valuemin and aria-valuemax. All three attributes work together to communicate the state of a range widget to assistive technologies.
Bad Example
<divrole="progressbar"
aria-valuenow=""
aria-valuemin="0"
aria-valuemax="100">
Loading...
</div>
Good Example — Known Value
<divrole="progressbar"
aria-valuenow="50"
aria-valuemin="0"
aria-valuemax="100">
50% complete
</div>
Good Example — Indeterminate State
<divrole="progressbar"
aria-valuemin="0"
aria-valuemax="100">
Loading...
</div>
In the indeterminate example, omitting aria-valuenow tells assistive technologies that the progress is ongoing but the exact value is not known.
A data: URI embeds resource content directly in a URL rather than pointing to an external file. It follows the format data:[<mediatype>][;base64],<data>. RFC 2397, which governs this scheme, explicitly states that fragment identifiers (the # character followed by a fragment name) are not valid in data: URIs.
This issue most commonly arises when developers try to reference a specific element inside an embedded SVG — for example, a particular <symbol> or element with an id — by appending a fragment like #icon-name to a data: URI. While fragments work in standard URLs (e.g., icons.svg#home), the data: URI scheme simply doesn't support them.
Why It Matters
- Standards compliance: Browsers may handle invalid
data:URIs inconsistently. Some may silently ignore the fragment, while others may fail to render the image entirely. - Portability: Code that relies on non-standard behavior in one browser may break in another or in a future update.
- Accessibility and tooling: Validators, linters, and assistive technologies expect well-formed URIs. An invalid URI can cause unexpected issues down the chain.
How to Fix It
You have several options depending on your use case:
- Remove the fragment from the
data:URI. If the embedded content is a complete, self-contained image, it doesn't need a fragment reference. - Inline the SVG directly into the HTML document. This lets you reference internal
idvalues with standard fragment syntax using<use>elements. - Use an external file instead of a
data:URI. Standard URLs likeicons.svg#homefully support fragment identifiers. - Encode the full, standalone SVG into the
data:URI so that it contains only the content you need, eliminating the need for a fragment reference.
Examples
❌ Incorrect: Fragment in a data: URI
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3Csymbol id='icon'%3E%3Ccircle cx='10' cy='10' r='10'/%3E%3C/symbol%3E%3C/svg%3E#icon"
alt="Icon">
The #icon fragment at the end of the data: URI violates RFC 2397 and triggers the validation error.
✅ Correct: Self-contained SVG in a data: URI (no fragment)
Embed only the content you need directly, without wrapping it in a <symbol> or referencing a fragment:
<img
src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='10'/%3E%3C/svg%3E"
alt="Icon">
✅ Correct: External SVG file with a fragment
Move the SVG to a separate file and reference the specific symbol using a standard URL fragment:
<imgsrc="icons.svg#icon"alt="Icon">
✅ Correct: Inline SVG with <use> referencing an internal id
If you need to reference individual symbols from a sprite, inline the SVG and use fragment references within the same document:
<svgxmlns="http://www.w3.org/2000/svg"hidden>
<symbolid="icon-home"viewBox="0 0 20 20">
<pathd="M10 2 L2 10 L4 10 L4 18 L16 18 L16 10 L18 10 Z"/>
</symbol>
</svg>
<svgwidth="20"height="20"aria-hidden="true">
<usehref="#icon-home"></use>
</svg>
This approach gives you full control over individual icons without needing data: URIs at all, and it's the most flexible option for icon systems.
In the structure of a URL, the @ symbol has a special meaning: it separates the userinfo component (username and password) from the host. A URL with credentials follows this pattern:
scheme://username:password@hostname/path
When the username or password itself contains an @ character — for example, an email address used as a username — the browser or URL parser may not be able to determine where the credentials end and the hostname begins. For instance, in http://user@name:pass@example.com, it's unclear whether the host is name or example.com.
The URL Standard (maintained by WHATWG) requires that any @ appearing within the userinfo component be percent-encoded as %40. Percent-encoding replaces the literal character with a % followed by its hexadecimal ASCII code (40 for @). This removes the ambiguity and ensures all parsers interpret the URL identically.
While modern browsers may attempt to handle ambiguous URLs, the behavior is not guaranteed to be consistent across all user agents, link checkers, or HTTP clients. Properly encoding these characters ensures reliable behavior everywhere and keeps your HTML valid.
Note: Including credentials directly in URLs is generally discouraged for security reasons, as they may be exposed in browser history, server logs, and referrer headers. Consider alternative authentication methods when possible.
Examples
❌ Incorrect: unencoded @ in the username
<ahref="http://user@name:password@example.com/path">Login</a>
Here, the parser cannot reliably distinguish user@name as the username from the @ that separates credentials from the host.
✅ Correct: percent-encoded @ in the username
<ahref="http://user%40name:password@example.com/path">Login</a>
The @ within the username is encoded as %40, leaving only one literal @ to serve as the delimiter before the hostname.
❌ Incorrect: unencoded @ in the password
<ahref="http://admin:p@ss@example.com/dashboard">Dashboard</a>
✅ Correct: percent-encoded @ in the password
<ahref="http://admin:p%40ss@example.com/dashboard">Dashboard</a>
❌ Incorrect: email address used as username without encoding
<ahref="ftp://joe@example.org:secret@ftp.example.com/files">Files</a>
✅ Correct: email address with @ percent-encoded
<ahref="ftp://joe%40example.org:secret@ftp.example.com/files">Files</a>
To fix this issue, identify every @ character that appears before the final @ in the authority section of the URL and replace it with %40. The last @ in the authority is the actual delimiter and must remain as a literal character.
The autocomplete attribute tells the browser how to handle autofilling a form field. The HTML specification defines a strict set of allowed values: the keywords on and off, and a collection of autofill field names such as name, email, username, new-password, street-address, and many others. The value "none" is not part of this specification, even though it might seem like a logical choice for "no autocomplete."
This confusion likely arises because some non-web APIs and frameworks use "none" as a keyword to disable features. In HTML, however, the correct keyword to disable autocompletion is "off". Using an invalid value like "none" leads to undefined browser behavior — some browsers may ignore it entirely and autofill anyway, while others might treat it as equivalent to "on". This inconsistency can cause unexpected user experiences and potential security concerns, especially for sensitive fields like passwords or credit card numbers.
Beyond standards compliance, using valid autocomplete values improves accessibility. Assistive technologies and password managers rely on recognized autofill field names to help users fill out forms efficiently. When a valid, descriptive value like "username" or "email" is provided, browsers and assistive tools can offer more accurate suggestions.
How to fix it
Replace "none" with the appropriate valid value:
- Use
"off"if you want to disable autofill for the field. - Use
"on"if you want the browser to decide how to autofill the field. - Use a specific autofill field name if you want to hint at the type of data expected.
Common autofill field names include: name, given-name, family-name, email, username, new-password, current-password, tel, street-address, postal-code, country, cc-number, cc-exp, and cc-name. You can also combine tokens, such as "shipping postal-code" or "billing cc-number", to provide additional context through section and hint tokens.
Note: Even with
autocomplete="off", some browsers may still autofill certain fields (particularly login credentials) for security or usability reasons. This is browser-specific behavior and not something the HTML specification can override.
Examples
Incorrect: using "none" to disable autofill
<form>
<labelfor="user">Username</label>
<inputtype="text"id="user"name="username"autocomplete="none">
</form>
Correct: using "off" to disable autofill
<form>
<labelfor="user">Username</label>
<inputtype="text"id="user"name="username"autocomplete="off">
</form>
Correct: using a specific autofill field name
When you know what kind of data a field collects, providing a descriptive autofill field name is often better than using "on" or "off". This helps browsers offer accurate suggestions:
<form>
<labelfor="user">Username</label>
<inputtype="text"id="user"name="username"autocomplete="username">
<labelfor="email">Email</label>
<inputtype="email"id="email"name="email"autocomplete="email">
<labelfor="pwd">New Password</label>
<inputtype="password"id="pwd"name="password"autocomplete="new-password">
</form>
Correct: using section and hint tokens
You can prefix an autofill field name with a section name or shipping/billing hint to distinguish between multiple addresses in the same form:
<form>
<labelfor="ship-zip">Shipping postal code</label>
<inputtype="text"id="ship-zip"name="ship_zip"autocomplete="shipping postal-code">
<labelfor="bill-zip">Billing postal code</label>
<inputtype="text"id="bill-zip"name="bill_zip"autocomplete="billing postal-code">
</form>
The autocomplete attribute does not accept "telephone" as a valid value — the correct value is "tel".
The autocomplete attribute helps browsers autofill form fields with previously saved user data. Each field type has a specific token defined in the HTML specification. For phone numbers, the valid token is "tel", not "telephone". Other related tokens include "tel-country-code", "tel-area-code", "tel-local", and "tel-extension" for more granular phone number parts.
Using the correct token ensures that browsers can properly suggest stored phone numbers to users, improving the form-filling experience.
Invalid Example
<labelfor="phone">Phone number</label>
<inputtype="tel"id="phone"name="phone"autocomplete="telephone">
Valid Example
<labelfor="phone">Phone number</label>
<inputtype="tel"id="phone"name="phone"autocomplete="tel">
The autocomplete attribute tells the browser how to handle autofill for a form field. The HTML specification defines a strict set of valid values, which include "on", "off", and a list of autofill field names such as "username", "new-password", "cc-number", "postal-code", and many others. When you use a value that isn't in this list — such as "nope", "false", "none", or any other made-up string — the W3C validator reports it as an invalid autofill field name.
A common reason developers use values like "nope" is as a workaround because some browsers historically ignored autocomplete="off". In older versions of Chrome and Firefox, the browser would still show autofill suggestions even when off was set, so developers discovered that using an unrecognized value like "nope" effectively tricked the browser into not showing suggestions. While this hack may have worked in practice, it produces invalid HTML and is not a reliable long-term solution since browser behavior around unrecognized values can change at any time.
Why this matters
- Standards compliance: Invalid attribute values make your HTML non-conforming, which can cause issues with tooling, testing pipelines, and accessibility auditors.
- Accessibility: Screen readers and assistive technologies rely on valid
autocompletevalues to help users fill in forms. Using a correct autofill field name like"given-name"or"email"can significantly improve the experience for users with disabilities. In fact, WCAG 2.1 Success Criterion 1.3.5 specifically recommends using valid autocomplete values for fields that collect user information. - Browser behavior: Modern browsers have improved their handling of
autocomplete="off". Using the standard value is now more reliable than it once was, and using it correctly ensures predictable behavior across browsers.
How to fix it
- To disable autocomplete, replace the invalid value with
"off". - To enable smart autofill, use the appropriate autofill field name from the HTML specification's list of autofill field names. This is the preferred approach for most user-facing forms.
- For new passwords (e.g., registration or password-change forms), use
"new-password"— this tells the browser to suggest a generated password rather than filling in a saved one.
Examples
Invalid: made-up autocomplete value
<inputtype="text"name="firstName"autocomplete="nope">
Other common invalid values that trigger the same error include "false", "none", "disable", and "no".
Fixed: disabling autocomplete with "off"
<inputtype="text"name="firstName"autocomplete="off">
Fixed: using a valid autofill field name
Using a specific autofill field name is often better than "off" because it helps browsers and assistive technologies understand the purpose of the field:
<inputtype="text"name="firstName"autocomplete="given-name">
Fixed: common valid autocomplete values in a form
<formmethod="post"action="/register">
<labelfor="name">Full Name</label>
<inputtype="text"id="name"name="name"autocomplete="name">
<labelfor="email">Email</label>
<inputtype="email"id="email"name="email"autocomplete="email">
<labelfor="newpass">Password</label>
<inputtype="password"id="newpass"name="password"autocomplete="new-password">
<labelfor="tel">Phone</label>
<inputtype="tel"id="tel"name="phone"autocomplete="tel">
<buttontype="submit">Register</button>
</form>
Some of the most commonly used valid values include: "name", "given-name", "family-name", "email", "username", "new-password", "current-password", "street-address", "postal-code", "country", "tel", "cc-number", and "organization". Refer to the full list in the HTML specification for all available options.
URLs must conform to the URL Living Standard, which forbids tab characters (U+0009) within the host/domain portion of a URL. While some browsers may silently strip tabs and still navigate to the intended destination, this behavior is not guaranteed and should not be relied upon.
This issue typically arises from one of the following scenarios:
- Copy-paste errors: Copying a URL from a document, email, or spreadsheet that inadvertently includes tab characters.
- Template or build tool formatting: A templating engine or code generator inserting whitespace (including tabs) into a URL string, especially when the URL is constructed across multiple lines.
- Manual typos: Accidentally pressing the Tab key while editing an
hrefvalue, particularly in editors that don't visualize whitespace.
Tab characters are invisible in most code editors by default, which makes this error frustrating to track down. Enabling "show whitespace" or "show invisible characters" in your editor can help you spot the offending character.
Why this matters
- Standards compliance: The HTML specification requires that
hrefvalues contain valid URLs. A tab in the domain makes the URL syntactically invalid. - Accessibility: Screen readers and assistive technologies parse
hrefvalues to announce link destinations. An invalid URL can lead to confusing or broken announcements. - Reliability: While major browsers tend to be forgiving and strip tabs before resolving URLs, some HTTP clients, crawlers, or older browsers may not. This can cause broken links in unexpected contexts like RSS readers, email clients, or web scrapers.
How to fix it
- Enable visible whitespace in your editor to locate tab characters.
- Search for tab characters in your
hrefvalues. In many editors, you can use a regex search for\twithin attribute values. - Remove the tab characters so the URL is a clean, continuous string with no embedded whitespace.
- If URLs are dynamically generated, inspect the code that builds them to ensure no tabs or other whitespace are concatenated into the domain.
Examples
❌ Incorrect: Tab character in the domain
In the example below, a tab character is embedded within the domain name (represented here as 	 for visibility, though in source code it would be an actual invisible tab):
<!-- The tab between "example" and ".com" causes the error -->
<ahref="https://example .com/page">Visit Example</a>
Note: The tab character between example and .com is invisible in most editors but triggers the validation error.
✅ Correct: Clean URL with no whitespace
<ahref="https://example.com/page">Visit Example</a>
❌ Incorrect: Tab introduced by multi-line URL construction in a template
This can happen when a URL is broken across lines in a template and tabs are used for indentation:
<ahref="https:// example.com/page">Visit Example</a>
example.com/page
✅ Correct: URL on a single line with no embedded whitespace
<ahref="https://example.com/page">Visit Example</a>
Tip: Finding hidden tabs
If you're having trouble locating the tab character, try pasting your href value into a tool that reveals character codes, or run a quick check in your browser's developer console:
// Check for tabs in a URL string
consturl=document.querySelector('a').getAttribute('href');
console.log(url.includes('\t'));// true if a tab is present
console.log(JSON.stringify(url));// shows \t explicitly in the output
The srcset attribute on img and source elements accepts a comma-separated list of image candidate strings. Each candidate consists of a URL optionally followed by a width descriptor (e.g., 300w) or a pixel density descriptor (e.g., 2x). The URL in each candidate must conform to the URL Standard, which does not permit raw square brackets in the query string of an HTTP or HTTPS URL.
This issue commonly arises when a backend framework or CMS generates URLs that use square brackets in query parameters — for example, ?filter[size]=large or ?dimensions[]=300. While many browsers are lenient and will load these URLs anyway, they are technically invalid according to the URL specification. Using invalid URLs can lead to unpredictable behavior across different browsers, HTML parsers, and tools that process your markup. It also means your HTML fails W3C validation, which can mask other, more critical issues in your code.
You have two main approaches to fix this:
Percent-encode the brackets. Replace every
[with%5Band every]with%5D. This preserves the intended query parameter structure while making the URL spec-compliant. Your server should interpret percent-encoded brackets identically to raw brackets.Eliminate brackets from the URL. If you control the server-side code, consider using alternative query parameter conventions that don't rely on brackets — for instance, using dot notation (
filter.size=large), comma-separated values (dimensions=300,400), or repeated parameter names (dimension=300&dimension=400).
When fixing these URLs, also make sure each image candidate follows the correct format: a valid URL, followed by optional whitespace and a descriptor, with candidates separated by commas.
Examples
Incorrect — raw square brackets in query string
This triggers the validation error because [ and ] appear unescaped in the srcset URLs:
<img
src="photo.jpg"
srcset="photo.jpg?crop[width]=400 400w, photo.jpg?crop[width]=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Fixed — percent-encoded brackets
Replacing [ with %5B and ] with %5D makes the URLs valid:
<img
src="photo.jpg"
srcset="photo.jpg?crop%5Bwidth%5D=400 400w, photo.jpg?crop%5Bwidth%5D=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Fixed — brackets removed from URL design
If you can modify the server-side routing, restructuring the query parameters avoids the issue entirely:
<img
src="photo.jpg"
srcset="photo.jpg?crop_width=400 400w, photo.jpg?crop_width=800 800w"
sizes="(max-width: 600px) 400px, 800px"
alt="A landscape photo">
Incorrect — brackets with pixel density descriptors
The same problem occurs regardless of whether you use width descriptors or density descriptors:
<img
src="avatar.jpg"
srcset="avatar.jpg?size=[sm] 1x, avatar.jpg?size=[lg] 2x"
alt="User avatar">
Fixed — percent-encoded version
<img
src="avatar.jpg"
srcset="avatar.jpg?size=%5Bsm%5D 1x, avatar.jpg?size=%5Blg%5D 2x"
alt="User avatar">
Fixed — simplified query parameters
<img
src="avatar.jpg"
srcset="avatar.jpg?size=sm 1x, avatar.jpg?size=lg 2x"
alt="User avatar">
The HTML specification defines that the width and height attributes on <iframe> elements must contain a valid non-negative integer — that is, a string of one or more digits representing a number zero or greater (e.g., "0", "300", "600"). When one of these attributes is set to an empty string (width="" or height=""), the validator raises this error because an empty string cannot be parsed as a valid integer.
This commonly happens when a CMS, template engine, or JavaScript framework outputs an <iframe> with a dynamic dimension value that ends up being blank. It can also occur when developers remove the value but leave the attribute in place, or when copy-pasting embed code and accidentally clearing the value.
While most browsers will fall back to their default iframe dimensions (typically 300×150 pixels) when they encounter an empty value, relying on this behavior is not standards-compliant. Invalid attribute values can cause unpredictable rendering across different browsers, interfere with layout calculations, and make your markup harder to maintain. Assistive technologies may also have trouble determining the intended dimensions of the iframe.
How to fix it
You have a few options:
- Set a valid integer value. If you know the desired dimensions, specify them directly as non-negative integers. The values represent pixels.
- Remove the attribute entirely. If you don't need to set dimensions via HTML attributes, remove the empty
widthorheightattribute. The browser will apply its default size, or you can control sizing with CSS. - Use CSS instead. For responsive designs or more flexible sizing, remove the HTML attributes and use CSS properties like
width,height,max-width, oraspect-ratio.
Note that these attributes accept only plain integers — no units, no percentages, and no decimal points. For example, width="600" is valid, but width="600px" or width="100%" is not.
Examples
❌ Invalid: empty string values
<iframesrc="https://example.com"width=""height=""></iframe>
Both width and height are set to empty strings, which are not valid non-negative integers.
✅ Fixed: specify valid integer values
<iframesrc="https://example.com"width="600"height="400"></iframe>
✅ Fixed: remove the empty attributes
<iframesrc="https://example.com"></iframe>
The browser will use its default dimensions (typically 300×150 pixels).
✅ Fixed: remove attributes and use CSS for sizing
<iframesrc="https://example.com"style="width:100%;height:400px;"></iframe>
This approach is especially useful for responsive layouts where a fixed pixel width in HTML doesn't make sense.
✅ Fixed: responsive iframe with CSS aspect ratio
<iframe
src="https://example.com/video"
style="width:100%;aspect-ratio:16/9;border: none;">
</iframe>
Using aspect-ratio in CSS lets the iframe scale responsively while maintaining its proportions, without needing width or height attributes at all.
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