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 aria-hidden attribute controls whether an element and its descendants are exposed to assistive technologies such as screen readers. When set to true, the element is hidden from the accessibility tree; when set to false, it remains visible. According to the WAI-ARIA specification, the only valid values for this attribute are the literal strings true and false. Any other value — including "true" with embedded quotation marks — is invalid.
When the validator reports a bad value like "true" (with the quotation marks as part of the value), it means the actual attribute value contains the characters "true" rather than just true. HTML attributes already use outer quotes as delimiters, so any quotes inside the value become part of the value itself. The browser or assistive technology may not recognize "true" as a valid ARIA state, which can lead to the element being incorrectly exposed to or hidden from screen readers, breaking the intended accessibility behavior.
This issue commonly arises in a few scenarios:
- Copy-pasting from formatted text where "smart quotes" or extra quoting gets included.
- Templating engines or frameworks that double-escape or double-quote attribute values (e.g.,
aria-hidden="{{value}}"where{{value}}already outputs"true"). - JavaScript that sets the attribute with extra quotes, such as
element.setAttribute("aria-hidden", '"true"').
To fix the issue, ensure the attribute value contains only the bare string true or false with no extra quotation marks, HTML entities, or escaped characters inside it.
Examples
Incorrect — extra quotes embedded in the value
<div aria-hidden='"true"'>
This content should be hidden from assistive tech
</div>
The rendered attribute value is literally "true" (five characters including the quotes), which is not a recognized ARIA value.
Incorrect — HTML entities producing extra quotes
<div aria-hidden=""true"">
This content should be hidden from assistive tech
</div>
The " entities resolve to quotation mark characters, producing the same invalid value of "true".
Correct — simple true value
<div aria-hidden="true">
This content is hidden from assistive tech
</div>
Correct — simple false value
<div aria-hidden="false">
This content is visible to assistive tech
</div>
Fixing the issue in JavaScript
If you're setting the attribute dynamically, make sure you aren't wrapping the value in extra quotes:
<div id="modal">Modal content</div>
<script>
// Incorrect:
// document.getElementById("modal").setAttribute("aria-hidden", '"true"');
// Correct:
document.getElementById("modal").setAttribute("aria-hidden", "true");
</script>
Fixing the issue in templating engines
If a template variable already outputs a quoted string, don't add additional quotes around it. For example, in a templating system:
<!-- Incorrect: if myVar outputs "true" (with quotes) -->
<!-- <div aria-hidden="{{myVar}}"> -->
<!-- Correct: ensure myVar outputs just true (no quotes) -->
<div aria-hidden="true">
Content
</div>
The key takeaway is straightforward: the outer quotes in aria-hidden="true" are HTML syntax — they delimit the attribute value. The value itself must be exactly true or false with nothing extra. If you're generating HTML dynamically, inspect the rendered output in your browser's developer tools to confirm the attribute value doesn't contain stray quotation marks.
The defer attribute on the <script> element is a boolean attribute and should not be assigned a value like "true".
In HTML, boolean attributes don't work like regular attributes. A boolean attribute is considered "true" simply by being present on the element, and "false" by being omitted entirely. Setting defer="true" is invalid because the only allowed values for a boolean attribute are either the empty string ("") or the attribute's own name (e.g., defer="defer").
The defer attribute tells the browser to download the script in parallel with HTML parsing and execute it only after the document has been fully parsed. It only works on scripts with a src attribute — it has no effect on inline scripts.
Bad Example
<script src="app.js" defer="true"></script>
Good Example
<script src="app.js" defer></script>
This rule applies to all boolean attributes in HTML, such as disabled, checked, readonly, async, hidden, and others. Just include the attribute name by itself — no value needed.
In HTML, boolean attributes work differently than you might expect from programming languages. They don't accept "true" or "false" as values. Instead, the presence of the attribute means it's active, and its absence means it's inactive. This is defined in the WHATWG HTML Living Standard, which states that a boolean attribute's value must either be the empty string or a case-insensitive match for the attribute's name.
This means there are exactly three valid ways to write the disabled attribute:
disabled(no value)disabled=""(empty string)disabled="disabled"(attribute name as value)
Any other value — including "true", "false", "yes", "no", or "1" — is invalid and will trigger a W3C validation error.
A common and dangerous misunderstanding is that disabled="false" will make an element not disabled. It won't. Because boolean attributes are activated by their presence alone, disabled="false" still disables the element. The browser sees the disabled attribute is present and treats the element as disabled, completely ignoring the "false" value. This can lead to confusing bugs where elements appear permanently disabled.
This issue affects all elements that support the disabled attribute, including <input>, <button>, <select>, <textarea>, <fieldset>, <optgroup>, and <option>. The same rules apply to other boolean attributes like checked, readonly, required, autofocus, and hidden.
Why this matters
- Standards compliance: Using invalid attribute values violates the HTML specification and produces W3C validation errors.
- Maintainability: Developers reading
disabled="true"ordisabled="false"may misunderstand the intent, especially if they assume"false"removes the disabled state. - Framework pitfalls: Some JavaScript frameworks dynamically set
disabled="true"ordisabled="false"as string values. When the rendered HTML reaches the browser, both values result in a disabled element, which is rarely the intended behavior.
How to fix it
- To disable an element, add the
disabledattribute with no value, or usedisabled=""ordisabled="disabled". - To enable an element, remove the
disabledattribute entirely. Don't set it to"false". - In JavaScript, use the DOM property
element.disabled = trueorelement.disabled = false, or useelement.removeAttribute('disabled')to enable it. Avoidelement.setAttribute('disabled', 'false').
Examples
❌ Invalid: using "true" and "false" as values
<form>
<input type="text" disabled="true">
<select disabled="false">
<option>Option A</option>
</select>
<button type="submit" disabled="true">Submit</button>
</form>
Both the "true" and "false" values are invalid. Additionally, disabled="false" still disables the <select> element, which is almost certainly not what was intended.
✅ Valid: correct boolean attribute usage
<form>
<input type="text" disabled>
<select>
<option>Option A</option>
</select>
<button type="submit" disabled="disabled">Submit</button>
</form>
Here, the <input> and <button> are disabled using valid syntax. The <select> is enabled because the disabled attribute has been removed entirely.
✅ Valid: toggling disabled state with JavaScript
<form>
<input type="text" id="username" disabled>
<button type="button" id="toggle">Enable field</button>
<script>
document.getElementById('toggle').addEventListener('click', function () {
var field = document.getElementById('username');
field.disabled = !field.disabled;
});
</script>
</form>
Using the disabled DOM property (a real boolean) is the correct way to toggle the disabled state dynamically. This avoids the pitfall of setting string values like "true" or "false" on the attribute.
The hidden attribute on a div (or any HTML element) does not accept "true" as a value. It is either a boolean attribute with no value, or it accepts the specific strings "", "hidden", or "until-found".
The hidden attribute controls whether an element is relevant to the page. When present as a plain boolean attribute (e.g., <div hidden>), the browser hides the element entirely and removes it from the accessibility tree. Setting hidden="true" is invalid because "true" is not one of the allowed states defined in the HTML specification.
The valid states are:
- An empty string (
"") or the keyword"hidden", both of which map to the "hidden" state. The element is not rendered and is excluded from the accessibility tree. - The keyword
"until-found", which hides the element visually but allows the browser to reveal it if the user searches for content within it (e.g., via Ctrl+F) or navigates to a fragment targeting it.
Since hidden is an enumerated attribute with these specific allowed values, "true" is treated as an invalid value. Most browsers happen to hide the element anyway when they encounter an unrecognized non-empty value, but the markup is still non-conforming.
Examples
Invalid: using hidden="true"
<div hidden="true">This content is hidden.</div>
Valid: using the boolean attribute or a valid keyword
<!-- Boolean attribute with no value -->
<div hidden>This content is hidden.</div>
<!-- Explicit "hidden" keyword (equivalent to the above) -->
<div hidden="hidden">This content is hidden.</div>
<!-- Hidden until found by in-page search or fragment navigation -->
<div hidden="until-found">This content is hidden until found.</div>
In HTML, boolean attributes work differently than you might expect from programming languages. A boolean attribute's presence on an element represents the true value, and its absence represents false. You don't set them to "true" or "false" like you would in JavaScript or other languages. According to the WHATWG HTML specification, a boolean attribute has exactly three valid forms:
- The attribute name alone (e.g.,
multiple) - The attribute with an empty value (e.g.,
multiple="") - The attribute with a value matching its own name, case-insensitively (e.g.,
multiple="multiple")
Setting multiple="true" is invalid because "true" is not one of the permitted values. While browsers are forgiving and will typically still treat the attribute as present (effectively enabling it), this produces a W3C validation error and does not conform to the HTML standard. Relying on browser leniency leads to inconsistent behavior, makes your code harder to maintain, and can cause problems with HTML processing tools or strict parsers.
This same rule applies to all boolean attributes in HTML, including disabled, readonly, checked, required, hidden, autoplay, and many others.
It's also important to note that multiple="false" does not disable the attribute. Because the attribute is still present on the element, the browser treats it as enabled. To disable a boolean attribute, you must remove it from the element entirely.
Examples
❌ Invalid: using "true" as the value
<label for="colors">Select your favorite colors:</label>
<select id="colors" name="colors" multiple="true">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
This triggers the validation error: Bad value "true" for attribute "multiple" on element "select".
✅ Fixed: attribute name only (preferred)
<label for="colors">Select your favorite colors:</label>
<select id="colors" name="colors" multiple>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
✅ Fixed: empty string value
<label for="colors">Select your favorite colors:</label>
<select id="colors" name="colors" multiple="">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
✅ Fixed: value matching the attribute name
<label for="colors">Select your favorite colors:</label>
<select id="colors" name="colors" multiple="multiple">
<option value="red">Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>
❌ Common mistake: using "false" to disable
<!-- This does NOT disable multiple selection — the attribute is still present -->
<select name="colors" multiple="false">
<option value="red">Red</option>
<option value="green">Green</option>
</select>
✅ Correct way to disable: remove the attribute entirely
<label for="color">Select a color:</label>
<select id="color" name="color">
<option value="red">Red</option>
<option value="green">Green</option>
</select>
In HTML, boolean attributes like selected work differently than you might expect if you're coming from a programming language. A boolean attribute's presence alone means "true," and its absence means "false." Setting selected="true" is invalid because the only permitted values for a boolean attribute are the empty string ("") or the attribute's own name (e.g., selected="selected"). The value "true" is not recognized by the HTML specification, which is why the W3C validator flags it.
This matters for several reasons. First, it violates the WHATWG HTML specification, which explicitly defines how boolean attributes must be written. Second, while most browsers are forgiving and will still treat selected="true" as if the option is selected, relying on this lenient behavior is risky — it can lead to inconsistencies across browsers or tools that parse HTML strictly. Third, invalid markup can cause problems for assistive technologies, automated testing tools, and server-side HTML processors that follow the spec closely.
The same rule applies to other boolean attributes like disabled, checked, readonly, multiple, required, and hidden. None of them should be set to "true" or "false".
It's also worth noting that setting a boolean attribute to "false" (e.g., selected="false") does not turn it off — the attribute's mere presence activates it. To deactivate a boolean attribute, you must remove it entirely from the element.
Examples
❌ Invalid: using selected="true"
<select name="color">
<option selected="true">Red</option>
<option>Green</option>
<option>Blue</option>
</select>
This triggers the validation error because "true" is not a valid value for the boolean selected attribute.
✅ Valid: bare attribute (preferred)
<select name="color">
<option selected>Red</option>
<option>Green</option>
<option>Blue</option>
</select>
The simplest and most common way to write a boolean attribute — just include the attribute name with no value.
✅ Valid: empty string value
<select name="color">
<option selected="">Red</option>
<option>Green</option>
<option>Blue</option>
</select>
An empty string is a valid value for any boolean attribute per the HTML spec.
✅ Valid: attribute name as value
<select name="color">
<option selected="selected">Red</option>
<option>Green</option>
<option>Blue</option>
</select>
Using the attribute's own name as its value is also valid. This form is sometimes seen in XHTML-style markup and in templating systems.
❌ Invalid: using selected="false" to deselect
<select name="color">
<option selected="false">Red</option>
<option>Green</option>
<option>Blue</option>
</select>
This is both invalid and misleading. The option will still be selected because the selected attribute is present. To not select an option, simply omit the attribute:
<select name="color">
<option>Red</option>
<option>Green</option>
<option>Blue</option>
</select>
The wrap attribute on a <textarea> controls how text is wrapped when the form is submitted. The value "virtual" was used by some older browsers (notably early versions of Netscape and Internet Explorer) as a proprietary alternative to what the HTML Standard now calls "soft". Since "virtual" was never part of any formal HTML specification, the W3C validator correctly rejects it as an invalid value.
The HTML Standard defines only two valid values for wrap:
soft(the default): The text is visually wrapped in the browser for display purposes, but no actual line break characters are inserted into the submitted form data. The server receives the text as continuous lines.hard: The browser inserts carriage return + line feed (CRLF) characters at the visual wrap points when the form is submitted, so the server receives the text with hard line breaks. When usingwrap="hard", you must also specify thecolsattribute so the browser knows where the wrap points are.
Since "virtual" was functionally identical to "soft", replacing it is straightforward. If you omit the wrap attribute altogether, the browser defaults to soft wrapping, which gives you the same behavior.
Why this matters
Using non-standard attribute values can lead to unpredictable behavior across browsers. While most modern browsers will likely fall back to soft wrapping when they encounter an unrecognized wrap value, this is not guaranteed by any specification. Sticking to valid values ensures consistent, cross-browser behavior and keeps your markup standards-compliant.
How to fix it
- Replace
wrap="virtual"withwrap="soft"for an explicit equivalent. - Remove the
wrapattribute entirely if you want the default soft wrapping behavior. - Use
wrap="hard"with acolsattribute if you actually need hard line breaks inserted on submission.
Examples
❌ Invalid: using the non-standard "virtual" value
<form>
<label for="msg">Message</label>
<textarea id="msg" name="msg" wrap="virtual"></textarea>
</form>
This triggers the error: Bad value "virtual" for attribute "wrap" on element "textarea".
✅ Fixed: using wrap="soft" (equivalent to "virtual")
<form>
<label for="msg">Message</label>
<textarea id="msg" name="msg" wrap="soft"></textarea>
</form>
✅ Fixed: omitting wrap entirely (defaults to "soft")
<form>
<label for="msg">Message</label>
<textarea id="msg" name="msg"></textarea>
</form>
Since "soft" is the default, removing the attribute produces identical behavior and cleaner markup.
✅ Fixed: using wrap="hard" with cols
If you need the submitted text to include line breaks at wrap points, use wrap="hard" and specify cols:
<form>
<label for="msg">Message</label>
<textarea id="msg" name="msg" wrap="hard" cols="60" rows="6"></textarea>
</form>
Note that cols is required when using wrap="hard". Omitting it will trigger a separate validation error.
Other legacy values to watch for
The value "virtual" isn't the only non-standard wrap value from the early web. You may also encounter wrap="physical" (the legacy equivalent of "hard") or wrap="off" (which disabled wrapping). Neither is valid in modern HTML. Replace "physical" with "hard" (and add cols), and replace "off" by removing the attribute and using CSS (white-space: nowrap; or overflow-wrap: normal;) to control visual wrapping if needed.
When you write an attribute like maxlength="200 and accidentally omit the closing quote, everything that follows — including subsequent attribute names and their values — gets absorbed into that one attribute's value. In this case, the validator sees the value of maxlength as 200 aria-required= (or similar), which is not a valid integer. The parser doesn't encounter a closing " until it finds the next quotation mark further along in the tag, causing a cascade of errors.
This is a problem for several reasons:
- Broken functionality: The
maxlengthattribute won't work because200 aria-required=is not a valid number. The browser cannot determine the intended character limit. - Lost attributes: The
aria-requiredattribute is swallowed into the malformedmaxlengthvalue, so it never gets applied as a separate attribute. Assistive technologies like screen readers won't know the field is required. - Accessibility impact: Since
aria-required="true"is lost, users who rely on screen readers won't receive the information that the field is mandatory, potentially leading to form submission errors and a frustrating experience.
The root cause is almost always a missing closing quotation mark. Carefully check that every attribute value has both an opening and a closing ". This kind of typo is easy to make and easy to miss, especially in long tags with many attributes.
Examples
Incorrect — missing closing quote on maxlength
The closing " after 200 is missing, so the value of maxlength extends all the way to the next quotation mark it finds:
<input type="text" name="nome" id="nome" maxlength="200 aria-required="true">
The validator interprets maxlength as having the value 200 aria-required=, and only true ends up as the value of an unintended or malformed attribute. Nothing works as expected.
Correct — properly quoted attributes
Each attribute has its own properly matched pair of quotation marks:
<input type="text" name="nome" id="nome" maxlength="200" aria-required="true">
Here, maxlength="200" correctly limits the input to 200 characters, and aria-required="true" is a separate attribute that tells assistive technologies the field is required.
Incorrect — missing closing quote with more attributes
This issue can happen with any combination of attributes. Here, a missing quote after maxlength absorbs class and placeholder:
<input type="text" maxlength="50 class="username" placeholder="Enter name">
Correct — all quotes properly closed
<input type="text" maxlength="50" class="username" placeholder="Enter name">
Tips for avoiding this issue
- Use a code editor with syntax highlighting. Most editors color attribute values differently from attribute names. If you see an attribute name rendered in the same color as a value string, a quote is likely missing.
- Format attributes one per line on complex elements. This makes it much easier to spot mismatched quotes:
<input
type="text"
name="nome"
id="nome"
maxlength="200"
aria-required="true">
- Validate early and often. Running your HTML through the W3C validator regularly helps catch these small typos before they cause confusing bugs in production.
The device-width and device-height media features (along with their min- and max- prefixed variants) originally referred to the physical dimensions of the device's entire screen, regardless of how much space was actually available to the document. In practice, this distinction caused confusion and inconsistent behavior across browsers. On most modern devices and browsers, device-width and width return the same value anyway, making the device-* variants redundant.
The device-* media features were frequently used as a proxy to detect mobile devices, but this was never a reliable approach. A narrow browser window on a desktop monitor would not trigger a max-device-width query even though the available layout space was small. Conversely, modern phones and tablets with high-resolution screens can report large device widths that don't reflect the actual CSS viewport size. The viewport-based alternatives (width, height, aspect-ratio) more accurately represent the space available for rendering your content.
Using deprecated media features causes W3C validation warnings and may eventually lose browser support entirely. Replacing them ensures your stylesheets are future-proof, standards-compliant, and behave consistently across all devices and window sizes.
How to fix it
Replace any device-width, device-height, or device-aspect-ratio media feature (including min- and max- prefixed versions) with the corresponding viewport-based equivalent:
| Deprecated feature | Replacement |
|---|---|
device-width | width |
min-device-width | min-width |
max-device-width | max-width |
device-height | height |
min-device-height | min-height |
max-device-height | max-height |
device-aspect-ratio | aspect-ratio |
The width media feature describes the width of the viewport (the targeted display area of the output device), including the size of a rendered scroll bar if any. This is the value you almost always want when writing responsive styles.
Examples
Incorrect: using deprecated max-device-width
This triggers the validation error because max-device-width is deprecated:
<link rel="stylesheet" media="only screen and (max-device-width: 768px)" href="mobile.css">
Correct: using max-width instead
Replace max-device-width with max-width to query the viewport width:
<link rel="stylesheet" media="only screen and (max-width: 768px)" href="mobile.css">
Incorrect: using deprecated min-device-width in a range
<link rel="stylesheet" media="screen and (min-device-width: 768px) and (max-device-width: 1024px)" href="tablet.css">
Correct: using viewport-based equivalents
<link rel="stylesheet" media="screen and (min-width: 768px) and (max-width: 1024px)" href="tablet.css">
Incorrect: using deprecated device-aspect-ratio
<link rel="stylesheet" media="screen and (device-aspect-ratio: 16/9)" href="widescreen.css">
Correct: using aspect-ratio
<link rel="stylesheet" media="screen and (aspect-ratio: 16/9)" href="widescreen.css">
Applying the same fix in CSS @media rules
The same deprecation applies to @media rules inside stylesheets. While the W3C validator specifically flags the media attribute on <link> elements, you should update your CSS as well:
/* Deprecated */
@media only screen and (max-device-width: 768px) {
.sidebar { display: none; }
}
/* Correct */
@media only screen and (max-width: 768px) {
.sidebar { display: none; }
}
If your site relies on a <meta name="viewport"> tag (as most responsive sites do), the viewport width already matches the device's CSS pixel width, so switching from device-width to width will produce identical results in virtually all cases.
The device-width, device-height, and device-aspect-ratio media features (including their min- and max- prefixed variants) refer to the physical dimensions of the entire screen, not the available space where your content is actually rendered. This distinction matters because the viewport size — the area your page occupies — can differ significantly from the full device screen size. For example, a browser window might not be maximized, or browser chrome might consume screen real estate. Using device-based queries means your responsive breakpoints may not match what users actually see.
These features were also commonly used as a proxy for detecting mobile devices, but this approach is unreliable. A small-screened laptop and a large tablet can have similar device widths, making device-based detection a poor heuristic. The W3C deprecated these features and recommends using viewport-based alternatives that more accurately reflect the rendering context of your document.
Beyond standards compliance, using deprecated media features can cause issues with future browser support. While current browsers still recognize them, there is no guarantee they will continue to do so. Replacing them now ensures your stylesheets remain functional and forward-compatible.
How to fix it
Replace each deprecated device-* media feature with its viewport-based equivalent:
| Deprecated feature | Replacement |
|---|---|
device-width | width |
min-device-width | min-width |
max-device-width | max-width |
device-height | height |
min-device-height | min-height |
max-device-height | max-height |
device-aspect-ratio | aspect-ratio |
min-device-aspect-ratio | min-aspect-ratio |
max-device-aspect-ratio | max-aspect-ratio |
The width media feature describes the width of the targeted display area of the output device. For continuous media, this is the width of the viewport including the size of a rendered scroll bar (if any). This is almost always the value you actually want when building responsive layouts.
Examples
Incorrect: using deprecated min-device-width
This triggers the validation error because min-device-width is deprecated:
<link rel="stylesheet" media="only screen and (min-device-width: 768px)" href="tablet.css">
Correct: using min-width instead
Replace min-device-width with min-width to query the viewport width:
<link rel="stylesheet" media="only screen and (min-width: 768px)" href="tablet.css">
Incorrect: using max-device-width for a mobile breakpoint
<link rel="stylesheet" media="screen and (max-device-width: 480px)" href="mobile.css">
Correct: using max-width
<link rel="stylesheet" media="screen and (max-width: 480px)" href="mobile.css">
Incorrect: using device-aspect-ratio
<link rel="stylesheet" media="screen and (device-aspect-ratio: 16/9)" href="widescreen.css">
Correct: using aspect-ratio
<link rel="stylesheet" media="screen and (aspect-ratio: 16/9)" href="widescreen.css">
Incorrect: combining multiple deprecated features
<link rel="stylesheet" media="screen and (min-device-width: 768px) and (max-device-width: 1024px)" href="tablet.css">
Correct: using viewport-based equivalents
<link rel="stylesheet" media="screen and (min-width: 768px) and (max-width: 1024px)" href="tablet.css">
The same replacements apply when these deprecated features appear in CSS @media rules or in the media attribute on <style> and <source> elements. Updating them across your entire codebase ensures consistent, standards-compliant responsive behavior.
The accept attribute provides browsers with a hint about which file types the user should be able to select through the file picker dialog. While browsers may still allow users to select other file types, the attribute helps filter the file picker to show relevant files first, improving the user experience.
The W3C validator reports this error when it encounters tokens in the accept attribute that don't conform to the expected format. The attribute value is parsed as a set of comma-separated tokens, and each token must be one of the following:
- A valid MIME type such as
application/pdf,text/plain, orimage/png(the/character separating type and subtype is required). - A file extension starting with a period, such as
.pdf,.docx, or.jpg. - One of three wildcard MIME types:
audio/*,video/*, orimage/*.
Common mistakes that trigger this error include using bare file extensions without the leading dot (e.g., pdf instead of .pdf), using arbitrary words that aren't valid MIME types, or including spaces in a way that creates malformed tokens. The HTML specification (WHATWG) defines strict rules for how these tokens are parsed, and violating them produces a validation error.
Getting this attribute right matters for several reasons. First, a correctly specified accept attribute helps users by pre-filtering the file picker, so they don't have to hunt through unrelated files. Second, assistive technologies may use this attribute to communicate accepted file types to users. Third, some browsers may silently ignore a malformed accept value entirely, removing the helpful filtering behavior you intended.
To fix the issue, review each token in your accept attribute and ensure it matches one of the three valid formats listed above. If you're unsure of the correct MIME type for a file format, consult the IANA Media Types registry. When in doubt, dot-prefixed file extensions (like .pdf or .docx) are often simpler and more readable.
Examples
Incorrect: bare words without dots or MIME type format
<input type="file" name="document" accept="doc, docx, pdf">
The tokens doc, docx, and pdf are neither valid MIME types (no /) nor valid file extensions (no leading .), so the validator rejects them.
Incorrect: spaces creating malformed tokens
<input type="file" name="photo" accept="image/png, image/jpeg">
While most browsers handle spaces after commas gracefully, the validator may flag this depending on parsing. It's safest to avoid spaces or keep them minimal.
Correct: using dot-prefixed file extensions
<input type="file" name="document" accept=".doc,.docx,.pdf">
Correct: using valid MIME types
<input type="file" name="document" accept="application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf">
Correct: mixing MIME types, extensions, and wildcards
<input type="file" name="media" accept="image/*,.pdf,video/mp4">
This accepts all image types, PDF files, and MP4 videos. Mixing formats is perfectly valid as long as each token individually conforms to the specification.
Correct: using wildcard MIME types for broad categories
<input type="file" name="photo" accept="image/*">
This allows the user to select any image file, regardless of specific format.
The aria-current attribute indicates that an element represents the current item within a container or set of related elements. It is designed to convey to assistive technology users what sighted users already perceive through visual styling — for example, a highlighted link in a navigation bar or the active step in a multi-step wizard.
Because assistive technologies rely on a known set of token values to communicate meaning, using an invalid value means the attribute is effectively meaningless to screen readers and other tools. This undermines accessibility for the users who need it most. Browsers and assistive technologies will not interpret an unrecognized value as true; they may ignore the attribute or treat it as false, leading to a confusing experience.
Common mistakes that trigger this error include:
- Using an empty string:
aria-current="" - Using a custom or misspelled value:
aria-current="active",aria-current="yes",aria-current="pgae" - Dynamically setting the attribute to
undefinedornullas a string
Accepted values
Each accepted value carries a specific semantic meaning. Use the most descriptive one that matches your context:
page— The current page within a set of pages (e.g., the active link in a breadcrumb or navigation menu).step— The current step within a process (e.g., the active step in a checkout flow).location— The current location within an environment or context (e.g., the highlighted node in a flow chart).date— The current date within a collection of dates (e.g., today's date in a calendar).time— The current time within a set of times (e.g., the current time slot in a timetable).true— A generic indication that the element is the current item, when none of the more specific values apply.false— Explicitly indicates the element is not the current item. This is equivalent to omitting the attribute entirely.
Examples
❌ Invalid: empty string
An empty string is not a valid value for aria-current:
<nav>
<ol>
<li><a href="/step-1" aria-current="">Step 1</a></li>
<li><a href="/step-2">Step 2</a></li>
</ol>
</nav>
❌ Invalid: custom keyword
Values like "active" or "yes" are not recognized:
<nav>
<ul>
<li><a href="/" aria-current="active">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
✅ Fixed: using page for navigation
When marking the current page in a navigation menu, page is the most appropriate value:
<nav>
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
✅ Fixed: using step for a multi-step process
<ol>
<li><a href="/checkout/cart">Cart</a></li>
<li><a href="/checkout/shipping" aria-current="step">Shipping</a></li>
<li><a href="/checkout/payment">Payment</a></li>
</ol>
✅ Fixed: using true as a generic fallback
When none of the specific token values fit, use true:
<ul>
<li><a href="/item-1" aria-current="true">Item 1</a></li>
<li><a href="/item-2">Item 2</a></li>
<li><a href="/item-3">Item 3</a></li>
</ul>
✅ Fixed: removing the attribute instead of setting false
If an element is not the current item, simply omit aria-current rather than setting it to false or an empty string. Both of the following are valid, but omitting the attribute is cleaner:
<!-- Valid but unnecessary -->
<a href="/about" aria-current="false">About</a>
<!-- Preferred: just omit it -->
<a href="/about">About</a>
Tips for dynamic frameworks
If you're using a JavaScript framework that conditionally sets aria-current, make sure the attribute is either set to a valid value or removed from the DOM entirely. Avoid patterns that render aria-current="" or aria-current="undefined" when the element is not current. In React, for instance, you can pass undefined (not the string "undefined") to omit the attribute:
<!-- What your framework should render for the current page -->
<a href="/" aria-current="page">Home</a>
<!-- What it should render for non-current pages (no attribute at all) -->
<a href="/about">About</a>
The aria-expanded attribute only accepts the values "true", "false", or "undefined", and it belongs on interactive elements, not on generic div elements.
The aria-expanded attribute indicates whether a grouping element controlled by the current element is expanded or collapsed. Valid values are "true" (the controlled element is expanded), "false" (the controlled element is collapsed), and "undefined" (the element does not own or control a groupable element). Any other value, such as "yes", "no", "0", "1", or an empty string, is invalid.
Beyond the attribute value itself, aria-expanded is meant for interactive elements like button, a (with href), or elements with an appropriate ARIA role such as role="button". A plain div has no implicit interactivity, so placing aria-expanded on it without an interactive role triggers a validation warning.
To fix this, use a valid value and place the attribute on an appropriate element. If you must use a div, give it an interactive role and make it keyboard accessible with tabindex.
Examples
Invalid usage
<div aria-expanded="yes">
<p>Panel content</p>
</div>
Two problems here: the value "yes" is not valid, and a plain div is not an interactive element.
Valid usage
<button aria-expanded="false" aria-controls="panel1">
Toggle panel
</button>
<div id="panel1" hidden>
<p>Panel content</p>
</div>
The aria-expanded="false" attribute sits on a button, which is interactive by default, and the value is one of the allowed strings. When the panel opens, update the attribute to "true" and remove the hidden attribute via JavaScript.
The aria-required attribute is a WAI-ARIA property that signals to assistive technologies (like screen readers) that a user must provide a value for a form control before the form can be submitted. According to the ARIA specification, the attribute's value must be either "true" or "false". Any other value — such as "yes", "1", "", or a misspelling — is invalid and will produce this validation error.
Common mistakes include writing aria-required="yes", aria-required="", aria-required="required", or even aria-required="True" (note: the value is case-sensitive and must be lowercase).
Why this matters
When an invalid value is used, assistive technologies may not correctly interpret whether the field is required. This can lead to a confusing experience for users who rely on screen readers, as they may not be told that a field is mandatory before submitting a form. Using valid attribute values ensures consistent, predictable behavior across all browsers and assistive technologies.
When to use aria-required vs. required
For native HTML form elements like <input>, <select>, and <textarea>, you should use the built-in HTML required attribute. It provides both validation behavior and accessibility information out of the box, without needing ARIA.
The aria-required attribute is intended for custom (non-semantic) form controls — for example, a <div> with a role="textbox" or role="combobox". In these cases, the browser doesn't know the element is a form control, so aria-required="true" communicates the requirement to assistive technologies.
Examples
❌ Invalid values for aria-required
<!-- "yes" is not a valid value -->
<div
role="textbox"
contenteditable
aria-labelledby="name_label"
aria-required="yes">
</div>
<!-- Empty string is not valid -->
<input type="text" aria-required="">
<!-- "required" is not valid -->
<input type="email" aria-required="required">
✅ Correct usage with aria-required
<div id="email_label">Email Address *</div>
<div
role="textbox"
contenteditable
aria-labelledby="email_label"
aria-required="true"
id="email">
</div>
✅ Explicitly marking a field as not required
<div id="notes_label">Notes (optional)</div>
<div
role="textbox"
contenteditable
aria-labelledby="notes_label"
aria-required="false"
id="notes">
</div>
✅ Preferred approach for native form elements
When using standard HTML form controls, skip aria-required and use the native required attribute instead:
<label for="email">Email Address *</label>
<input type="email" id="email" name="email" required>
<label for="country">Country *</label>
<select id="country" name="country" required>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</select>
The native required attribute automatically conveys the required state to assistive technologies and also triggers built-in browser validation, making it the better choice whenever a native form element is available.
The HTML specification defines boolean attributes as attributes whose presence indicates a true state and whose absence indicates false. According to the WHATWG HTML standard, a boolean attribute may only have three valid representations:
- The attribute name alone (e.g.,
async) - The attribute with an empty string value (e.g.,
async="") - The attribute with a value matching its own name, case-insensitively (e.g.,
async="async")
Any other value — such as async="true", async="1", async="yes", or async="false" — is invalid HTML and will trigger this validation error. This is a common misunderstanding because developers often assume boolean attributes work like boolean values in programming languages, where you'd assign true or false.
Why this matters
While most browsers are lenient and will treat any value of async as the true state (since the attribute is present regardless of its value), using invalid values creates several problems:
- Standards compliance: Invalid HTML may cause issues with strict parsers, validators, or tools that process your markup.
- Misleading intent: Writing
async="false"does not disable async behavior — the attribute is still present, so the browser treats it as enabled. This can lead to confusing bugs where a script behaves asynchronously even though the developer intended otherwise. - Maintainability: Other developers reading the code may misinterpret
async="false"as actually disabling async loading.
To disable async behavior, you must remove the attribute entirely rather than setting it to "false".
How async works
For classic scripts with a src attribute, the async attribute causes the script to be fetched in parallel with HTML parsing and executed as soon as it's available, without waiting for the document to finish parsing.
For module scripts (type="module"), the async attribute causes the module and all its dependencies to be fetched in parallel and executed as soon as they are ready, rather than waiting until the document has been parsed (which is the default deferred behavior for modules).
Examples
❌ Invalid: arbitrary values on async
<!-- Bad: "true" is not a valid boolean attribute value -->
<script async="true" src="app.js"></script>
<!-- Bad: "1" is not a valid boolean attribute value -->
<script async="1" src="analytics.js"></script>
<!-- Bad: "yes" is not a valid boolean attribute value -->
<script async="yes" src="tracker.js"></script>
<!-- Bad and misleading: this does NOT disable async -->
<script async="false" src="app.js"></script>
✅ Valid: correct boolean attribute usage
<!-- Preferred: attribute name alone -->
<script async src="app.js"></script>
<!-- Also valid: empty string value -->
<script async="" src="app.js"></script>
<!-- Also valid: value matching attribute name -->
<script async="async" src="app.js"></script>
<!-- Correct way to disable async: remove the attribute -->
<script src="app.js"></script>
✅ Valid: async with module scripts
<script async type="module" src="app.mjs"></script>
<script async type="module">
import { init } from './utils.mjs';
init();
</script>
This same rule applies to all boolean attributes in HTML, including defer, disabled, checked, required, hidden, and others. When in doubt, use the attribute name on its own with no value — it's the cleanest and most widely recognized form.
In HTML, boolean attributes work differently from regular attributes. They don't take true or false as values. Instead, the mere presence of the attribute represents the "true" state, and its complete absence represents "false." According to the WHATWG HTML specification, a boolean attribute has exactly three valid representations: the attribute name alone (checked), an empty string value (checked=""), or the attribute name as its own value (checked="checked"). Any other value — including seemingly intuitive ones like "true", "false", "yes", or "no" — is invalid.
This is particularly important to understand because checked="false" does not uncheck the input. Since the attribute is still present, the browser interprets it as checked. This can lead to confusing behavior where a developer writes checked="false" expecting the checkbox to be unchecked, but it renders as checked. To leave an input unchecked, you must omit the checked attribute entirely.
The checked attribute applies to <input> elements of type checkbox and radio. It sets the initial checked state when the page loads. Note that JavaScript uses a different convention — the checked DOM property accepts true or false as JavaScript boolean values — but this does not apply to HTML markup.
Fixing this issue ensures standards compliance, avoids unexpected behavior, and improves code clarity. While browsers are lenient and will typically treat any value as "checked," relying on this behavior is non-standard and can cause confusion for developers reading the code.
Examples
Invalid: using a string value
<input type="checkbox" checked="true">
<input type="radio" name="color" value="red" checked="yes">
These produce validation errors because "true" and "yes" are not valid boolean attribute values.
Invalid: attempting to set false
<!-- This does NOT uncheck the input — it's still checked! -->
<input type="checkbox" checked="false">
Despite the value "false", the checkbox will still render as checked because the attribute is present.
Valid: attribute name only (recommended)
<input type="checkbox" checked>
<input type="radio" name="color" value="red" checked>
This is the most common and cleanest form.
Valid: empty string value
<input type="checkbox" checked="">
Valid: attribute name as its own value
<input type="checkbox" checked="checked">
This form is sometimes seen in XHTML-compatible markup.
Valid: omitting the attribute to leave unchecked
<input type="checkbox">
To represent an unchecked state, simply leave the attribute off.
Full example in context
<!DOCTYPE html>
<html lang="en">
<head>
<title>Newsletter Preferences</title>
</head>
<body>
<form>
<fieldset>
<legend>Email preferences</legend>
<label>
<input type="checkbox" name="newsletter" checked>
Subscribe to newsletter
</label>
<label>
<input type="checkbox" name="promotions">
Receive promotional emails
</label>
</fieldset>
<fieldset>
<legend>Frequency</legend>
<label>
<input type="radio" name="frequency" value="daily" checked>
Daily
</label>
<label>
<input type="radio" name="frequency" value="weekly">
Weekly
</label>
</fieldset>
</form>
</body>
</html>
In this example, the newsletter checkbox and the "Daily" radio button are pre-selected using the valid checked attribute without any value. The promotions checkbox and "Weekly" radio button are unchecked because the attribute is absent.
The <time> HTML element represents a specific period in time or a duration. Its datetime attribute translates human-readable text into a machine-readable format, enabling browsers, search engines, and assistive technologies to reliably parse and understand temporal data. When the datetime value doesn't match one of the accepted formats, the machine-readable purpose of the element is defeated — tools cannot interpret the date or time, which undermines features like search engine rich results, calendar integration, and accessibility enhancements for screen readers.
The HTML specification defines several valid formats for the datetime attribute. Here are the most commonly used ones:
| Format | Example | Description |
|---|---|---|
| Date | 2024-03-15 | Year, month, day |
| Month | 2024-03 | Year and month only |
| Year | 2024 | Valid year |
| Yearless date | 03-15 | Month and day without year |
| Time | 14:30 or 14:30:00 | Hours and minutes (seconds optional) |
| Date and time | 2024-03-15T14:30 | Date and time separated by T |
| Date and time with timezone | 2024-03-15T14:30Z or 2024-03-15T14:30+05:30 | With UTC (Z) or offset |
| Duration (precise) | PT1H30M | ISO 8601 duration |
| Duration (approximate) | P2Y6M | Years, months, etc. |
| Week | 2024-W12 | ISO week number |
Common mistakes that trigger this error include:
- Using slashes instead of hyphens:
03/15/2024instead of2024-03-15 - Using informal date formats:
March 15, 2024or15-03-2024 - Omitting the
Tseparator between date and time:2024-03-15 14:30 - Using 12-hour time with AM/PM:
2:30 PMinstead of14:30 - Providing incomplete values:
2024-3-5instead of2024-03-05(months and days must be zero-padded)
Examples
Invalid: Slash-separated date
<time datetime="03/15/2024">March 15, 2024</time>
Valid: ISO 8601 date format
<time datetime="2024-03-15">March 15, 2024</time>
Invalid: Missing T separator and using AM/PM
<time datetime="2024-03-15 2:30 PM">March 15 at 2:30 PM</time>
Valid: Date-time with T separator and 24-hour time
<time datetime="2024-03-15T14:30">March 15 at 2:30 PM</time>
Invalid: Informal time string
<time datetime="half past two">2:30 PM</time>
Valid: Simple time value
<time datetime="14:30">2:30 PM</time>
Invalid: Non-standard duration
<time datetime="1 hour 30 minutes">1.5 hours</time>
Valid: ISO 8601 duration
<time datetime="PT1H30M">1.5 hours</time>
Valid: Date-time with timezone offset
<p>The event starts at <time datetime="2024-03-15T14:30-05:00">2:30 PM ET on March 15</time>.</p>
Valid: Using only the month
<p>Published in <time datetime="2024-03">March 2024</time>.</p>
Remember that the human-readable text content between the <time> tags can be in any format you like — it's only the datetime attribute value that must follow the specification. This lets you display dates in a user-friendly way while still providing a standardized machine-readable value.
Boolean attributes in HTML work differently from regular attributes. Their mere presence on an element makes them "true," and their absence makes them "false." According to the WHATWG HTML specification, a boolean attribute may only have three valid representations:
- The attribute name alone (e.g.,
disabled) - The attribute with an empty string value (e.g.,
disabled="") - The attribute with its own name as the value (e.g.,
disabled="disabled")
Any other value — including seemingly intuitive ones like "true", "yes", or "no" — is invalid and will cause the W3C HTML Validator to report an error such as: Bad value "disabled" for attribute "disabled" on element "input" (or a similar message referencing whatever invalid value you used).
Why this matters
Standards compliance: Using invalid values violates the HTML specification, which can lead to unpredictable behavior as browsers evolve.
Misleading behavior: A common pitfall is writing disabled="false" and expecting the input to be enabled. This does not work as expected — because the attribute is still present, the element remains disabled regardless of the value. This can lead to confusing bugs where developers think they're enabling a field but it stays disabled.
Accessibility: Assistive technologies rely on the DOM's interpretation of boolean attributes. While browsers typically handle invalid values gracefully by treating any present disabled attribute as true, sticking to valid values ensures the most consistent behavior across screen readers and other tools.
Templating and frameworks: This issue frequently arises when templating engines or JavaScript frameworks insert string values into boolean attributes. If your template outputs disabled="true" or disabled="false", you should instead conditionally include or omit the attribute entirely.
How to fix it
- Remove the value entirely — just write the attribute name by itself.
- Use an empty string — write
disabled=""if your tooling requires an explicit value. - Use the canonical form — write
disabled="disabled"if you need XHTML compatibility. - To enable an element, remove the
disabledattribute completely rather than setting it to"false".
Examples
Incorrect usage
These all trigger a validation error because the values are not valid for a boolean attribute:
<input type="text" disabled="yes">
<input type="text" disabled="true">
<input type="text" disabled="false">
<input type="text" disabled="1">
<button disabled="no">Submit</button>
Note that disabled="false" and disabled="no" still disable the element — the browser sees the attribute is present and treats it as true.
Correct usage
All three of these are valid ways to disable an input:
<input type="text" disabled>
<input type="text" disabled="">
<input type="text" disabled="disabled">
To have an enabled input, simply omit the attribute:
<input type="text">
Handling dynamic values in JavaScript
If you need to toggle the disabled state dynamically, use the DOM property rather than setting an attribute value:
<form>
<input type="text" id="username">
<button type="button" id="toggle">Toggle</button>
<script>
document.getElementById("toggle").addEventListener("click", function () {
var input = document.getElementById("username");
input.disabled = !input.disabled;
});
</script>
</form>
Setting element.disabled = true or element.disabled = false in JavaScript correctly adds or removes the attribute from the DOM without producing invalid markup.
Other boolean attributes
This same rule applies to all boolean attributes in HTML, including checked, readonly, required, hidden, autoplay, loop, muted, and others. For example:
<!-- Incorrect -->
<input type="checkbox" checked="true">
<input type="email" required="required_field">
<!-- Correct -->
<input type="checkbox" checked>
<input type="email" required>
When in doubt, use the simplest form: just the attribute name with no value. It's the most readable, the most concise, and fully compliant with the HTML specification.
The <label> element represents a caption for a form control. When you use the for attribute, its value must match the id of the form control it labels. According to the HTML specification, an id attribute value must not contain any whitespace characters — this includes spaces, tabs, line feeds, carriage returns, and form feeds. Since for must reference a valid ID, the same restriction applies to its value.
This error typically occurs when a developer uses a space-separated name (like "user name" or "first name") instead of a single continuous token. Browsers may fail to establish the programmatic association between the label and its form control when the for value contains whitespace. This directly impacts accessibility: screen readers rely on this association to announce the label text when a user focuses on the input. It also breaks the click-to-focus behavior where clicking a label moves focus to its associated control.
To fix this issue, replace any whitespace in the for attribute value with a valid character such as a hyphen (-), underscore (_), or camelCase. Make sure the id on the corresponding form control matches exactly.
Examples
Incorrect — whitespace in the for attribute
<form>
<label for="user name">Name</label>
<input type="text" id="user name">
</form>
The value "user name" contains a space, which makes it an invalid ID reference. The validator will report: Bad value "user name" for attribute "for" on element "label": An ID must not contain whitespace.
Correct — using an underscore instead of a space
<form>
<label for="user_name">Name</label>
<input type="text" id="user_name">
</form>
Correct — using a hyphen instead of a space
<form>
<label for="user-name">Name</label>
<input type="text" id="user-name">
</form>
Correct — using camelCase
<form>
<label for="userName">Name</label>
<input type="text" id="userName">
</form>
Incorrect — leading or trailing whitespace
Whitespace doesn't have to be in the middle of the value to trigger this error. Leading or trailing spaces also make the ID invalid:
<form>
<label for=" email">Email</label>
<input type="text" id=" email">
</form>
This can be easy to miss when values are generated dynamically or copied from another source. Trim the value to fix it:
<form>
<label for="email">Email</label>
<input type="text" id="email">
</form>
Alternative — wrapping the input inside the label
If you wrap the form control inside the <label> element, you don't need the for attribute at all. The association is implicit:
<form>
<label>
Name
<input type="text">
</label>
</form>
This approach avoids potential ID mismatches entirely, though explicit for/id pairing is often preferred for flexibility in layout and styling.
The HTML specification defines the height and width attributes on the <embed> element as valid non-negative integers. This means the value must consist only of digits — for example, 650 — with no units, whitespace, or other characters appended. When you write height="650px", the validator encounters the letter "p" where it expects either another digit or the end of the value, and it raises this error.
This is a common mistake because CSS requires units (e.g., 650px), and it's easy to assume HTML attributes work the same way. They don't. In HTML, the height attribute implicitly means CSS pixels, so writing 650 already means "650 pixels." Adding px is not only redundant — it makes the value invalid.
While most browsers are forgiving and will parse 650px correctly by stripping the unit, relying on this behavior is problematic. It violates the HTML specification, causes validation errors that can mask other real issues in your markup, and there's no guarantee every browser or embedded content handler will be equally tolerant. Standards compliance ensures consistent rendering across browsers and assistive technologies.
How to fix it
You have two approaches:
Remove the unit from the HTML attribute. Change
height="650px"toheight="650". This is the simplest fix and keeps your sizing in the markup.Move sizing to CSS. Remove the
heightattribute entirely and use a stylesheet or inlinestyleattribute instead. This approach is more flexible because CSS supports units like%,em,vh, and more.
The same rule applies to the width attribute on <embed>, as well as height and width on elements like <img>, <video>, <iframe>, and <canvas> — all of which expect plain integers in HTML.
Examples
❌ Invalid: unit included in the HTML attribute
The px suffix causes the validator error because the attribute value must be digits only.
<embed src="file.pdf" type="application/pdf" width="800" height="650px">
Other invalid variations include:
<embed src="file.pdf" type="application/pdf" height="100%">
<embed src="file.pdf" type="application/pdf" height="40em">
<embed src="file.pdf" type="application/pdf" height="50vh">
✅ Fixed: plain integer without a unit
Remove the unit so the value is a valid non-negative integer.
<embed src="file.pdf" type="application/pdf" width="800" height="650">
✅ Fixed: sizing moved to CSS
If you need units other than pixels, or prefer to keep presentation in your stylesheets, use CSS instead of the HTML attribute.
<embed class="pdf-viewer" src="file.pdf" type="application/pdf">
.pdf-viewer {
width: 800px;
height: 650px;
}
✅ Fixed: inline style as an alternative
You can also use the style attribute directly if a separate stylesheet isn't practical.
<embed src="file.pdf" type="application/pdf" style="width: 800px; height: 80vh;">
This is especially useful when you need viewport-relative or percentage-based sizing that HTML attributes can't express.
The height attribute on <iframe> is defined in the HTML specification as a "valid non-negative integer." This means the value must be a string of one or more digit characters (0 through 9) and nothing else. Unlike CSS properties, this attribute does not accept units like px or %, nor does it accept decimal values like 315.5. Even invisible characters such as leading or trailing spaces will trigger this validation error because the parser expects a digit and encounters something else.
This matters for several reasons. While browsers are generally forgiving and may still render the iframe correctly despite an invalid value, relying on error recovery behavior is fragile and may not work consistently across all browsers or future versions. Invalid attribute values can also cause unexpected results in automated tools, screen readers, and other user agents that parse HTML strictly. Writing valid, standards-compliant markup ensures predictable behavior everywhere.
Common causes of this error include:
- Adding CSS units to the attribute value (e.g.,
height="315px") - Using percentages (e.g.,
height="100%") - Decimal values (e.g.,
height="315.5") - Whitespace before or after the number (e.g.,
height=" 315"orheight="315 ") - Copy-paste artifacts introducing hidden characters
If you need percentage-based or decimal sizing, use CSS instead of the HTML attribute. The height attribute only accepts whole pixel values.
Examples
❌ Invalid: Using px unit in the attribute
<iframe width="560" height="315px" src="https://example.com/video"></iframe>
The validator sees the p character after 315 and reports the error.
❌ Invalid: Using a percentage
<iframe width="100%" height="100%" src="https://example.com/video"></iframe>
Percentage values are not allowed in the height attribute.
❌ Invalid: Decimal value
<iframe width="560" height="315.5" src="https://example.com/video"></iframe>
The decimal point is not a digit, so the validator rejects it.
❌ Invalid: Leading or trailing whitespace
<iframe width="560" height=" 315 " src="https://example.com/video"></iframe>
The space before 315 is encountered where a digit is expected.
✅ Valid: Digits only
<iframe width="560" height="315" src="https://example.com/video"></iframe>
The value 315 contains only digits and is a valid non-negative integer.
✅ Valid: Using CSS for percentage-based sizing
If you need the iframe to scale responsively, remove the height attribute and use CSS instead:
<iframe
width="560"
height="315"
src="https://example.com/video"
style="width: 100%; height: 100%;"
></iframe>
Or better yet, apply styles through a stylesheet:
<style>
.responsive-iframe {
width: 100%;
height: 400px;
}
</style>
<iframe class="responsive-iframe" src="https://example.com/video"></iframe>
This keeps the HTML attributes valid while giving you full control over sizing through CSS, including support for units like %, vh, em, and calc().
The height attribute on <video> is defined in the HTML specification as a "valid non-negative integer." This means it must be a string of digits (e.g., "480") with no units, no decimal points, and no percentage signs. When you write something like height="50%", the W3C validator rejects it because % is not a digit character and percentage values are not part of the attribute's allowed syntax.
This same rule applies to the width attribute on <video>, as well as the width and height attributes on <img>, <canvas>, and other elements that accept pixel dimensions. The integer you provide represents a size in CSS pixels, so height="480" means 480 CSS pixels — no unit suffix is needed or allowed.
Why this matters
- Standards compliance: The HTML living standard explicitly defines these attributes as non-negative integers. Using percentages violates the spec and produces a validation error.
- Browser behavior: While some browsers may attempt to interpret a percentage value, the behavior is undefined and inconsistent. You cannot rely on it working correctly across all browsers and versions.
- Layout stability: Providing valid
widthandheightinteger values helps browsers reserve the correct aspect ratio for the video before it loads, reducing cumulative layout shift (CLS). Invalid values undermine this benefit.
How to fix it
- Replace the percentage value with a plain integer representing the desired pixel height.
- If you need percentage-based or responsive sizing, remove the percentage from the HTML attribute and use CSS instead. CSS
widthandheightproperties fully support percentage values, viewport units, and other flexible sizing methods. - Always keep integer
widthandheightattributes on the element when possible, even if you also use CSS for responsive sizing. This provides an intrinsic aspect ratio hint to the browser.
Examples
❌ Invalid: percentage value in the height attribute
<video controls width="100%" height="50%">
<source src="/media/video.webm" type="video/webm">
</video>
The validator reports: Bad value "50%" for attribute "height" on element "video": Expected a digit but saw "%" instead. The same error applies to the width="100%" attribute.
✅ Fixed: integer values for intrinsic dimensions
<video controls width="640" height="480">
<source src="/media/video.webm" type="video/webm">
</video>
Both width and height use plain integers representing CSS pixels. This is valid and gives the browser an aspect ratio hint (4:3 in this case).
✅ Fixed: responsive sizing with CSS
If you need the video to scale as a percentage of its container, use CSS for the sizing while keeping valid integer attributes for the aspect ratio:
<video controls width="640" height="360" style="width: 100%; height: auto;">
<source src="/media/video.webm" type="video/webm">
</video>
Here, width="640" and height="360" tell the browser the video's intrinsic aspect ratio (16:9), while the inline style (or an external stylesheet) makes the video fill 100% of its container width and scale its height proportionally. This approach is both valid HTML and fully responsive.
✅ Fixed: using a CSS class for responsive video
<style>
.responsive-video {
width: 100%;
max-width: 800px;
height: auto;
}
</style>
<video controls width="800" height="450" class="responsive-video">
<source src="/media/video.webm" type="video/webm">
</video>
This keeps the HTML valid, provides an aspect ratio hint, and achieves flexible, percentage-based sizing entirely through CSS.
The href attribute on an <a> element must contain a valid URL as defined by the WHATWG URL Standard. According to this standard, the forward slash (/) is the only recognized path segment delimiter. Backslashes (\) have no defined role in URL path syntax and are treated as invalid characters by the validator.
This issue most commonly occurs when developers copy file paths from Windows, which uses backslashes as its native path separator, directly into HTML. For example, copying a path like images\photos\sunset.jpg from Windows Explorer and pasting it into an href attribute will trigger this validation error.
While most modern browsers will silently normalize backslashes to forward slashes, relying on this behavior is problematic for several reasons:
- Standards compliance: Your HTML fails validation, which can mask other real issues in your code.
- Interoperability: Not all HTTP clients, crawlers, or tools normalize backslashes. Search engine bots, link checkers, or older browsers may fail to follow the link correctly.
- Portability: Code that depends on browser error correction is fragile and may break in unexpected environments, such as server-side rendering, email clients, or embedded web views.
- Accessibility: Screen readers and assistive technologies that parse
hrefvalues may not handle backslashes consistently, potentially breaking navigation for users who rely on these tools.
To fix the issue, simply replace every backslash (\) with a forward slash (/) in any URL used in an href attribute. This applies not only to <a> elements but to any attribute that expects a URL, such as src, action, or data.
Examples
Incorrect: backslash used as path delimiter
<a href="docs\guide\intro.html">Introduction</a>
Correct: forward slash used as path delimiter
<a href="docs/guide/intro.html">Introduction</a>
Incorrect: backslashes in an absolute URL
<a href="https://example.com\blog\2024\post.html">Read the post</a>
Correct: forward slashes in an absolute URL
<a href="https://example.com/blog/2024/post.html">Read the post</a>
Incorrect: mixed slashes
Sometimes a URL contains a mix of forward and backslashes, which still triggers the error:
<a href="assets/images\photo.jpg">View photo</a>
Correct: all forward slashes
<a href="assets/images/photo.jpg">View photo</a>
Tips for avoiding this issue
- Search and replace: If you're migrating content or working with paths generated on Windows, do a global find-and-replace of
\with/across your HTML files. - Editor settings: Many code editors can highlight or auto-fix invalid URL characters. Enable linting tools or HTML validation plugins to catch this early.
- Build tools: If your build process generates links from file system paths, ensure it normalizes path separators to forward slashes before writing them into HTML output.
- URL encoding: If you genuinely need a literal backslash character within a URL (which is extremely rare), it must be percent-encoded as
%5C. However, this is almost never the intended behavior when this validation error appears.
A valid URL consists of several parts: a scheme (like https), followed by ://, then the host, and optionally a path, query string, and fragment. The :// separator — a colon followed by two forward slashes — is a required part of the URL syntax for schemes like http and https. When one of these slashes is missing, the browser may fail to navigate to the intended destination, interpret the value as a relative path, or behave unpredictably across different environments.
This error commonly occurs due to simple typos, copy-paste mistakes, or programmatic URL construction where string concatenation goes wrong. While some browsers may attempt to correct malformed URLs, you should never rely on this behavior. A malformed href can break navigation entirely, cause security warnings, produce unexpected redirects, or confuse assistive technologies like screen readers that announce link destinations to users.
Beyond the missing-slash case, this error can also appear when other parts of the URL contain characters that aren't valid without proper encoding — for instance, spaces or special characters that should be percent-encoded. Always ensure your URLs conform to the URL Standard.
How to Fix
- Check the scheme separator: Verify that the protocol is followed by
://(colon and two slashes). For example,https://nothttps:/orhttps:. - Validate the full URL: Paste the URL into a browser's address bar to confirm it resolves correctly.
- Encode special characters: If the URL contains spaces or special characters, use proper percent-encoding (e.g., spaces become
%20). - Review dynamically generated URLs: If URLs are built through string concatenation or template logic, double-check that all parts are joined correctly.
Examples
Incorrect: Missing a slash after the scheme
<a href="https:/example.com">Visit Example</a>
The validator reports this because https:/example.com has only one slash after the colon instead of the required two.
Incorrect: Missing both slashes
<a href="https:example.com">Visit Example</a>
This is also invalid — the colon must be followed by // for https URLs.
Correct: Properly formatted URL
<a href="https://example.com">Visit Example</a>
Correct: URL with a path
<a href="https://example.com/blog/my-post">Read the Post</a>
Correct: URL with encoded spaces
<a href="https://example.com/search?q=hello%20world">Search</a>
Incorrect: Unencoded space in URL
<a href="https://example.com/my page">My Page</a>
Spaces are not valid in URLs. Use %20 or + (in query strings) instead:
<a href="https://example.com/my%20page">My Page</a>
A mailto: link in an href attribute contains a [ character that is not valid in a URI according to RFC 3986.
Square brackets ([ and ]) are reserved characters in URIs and must be percent-encoded when used outside their specific purpose (IPv6 address literals). If the email address or other part of the mailto: URI contains a [, it must be encoded as %5B. Similarly, ] must be encoded as %5D.
This error typically appears when a mailto: link includes square brackets in the subject, body, or display-related parts of the URL. Query parameters in mailto: links — such as ?subject= or ?body= — must have their values properly percent-encoded.
Invalid example
<a href="mailto:info@example.com?subject=[Action Required] Review">
Email us
</a>
Valid example
Percent-encode the square brackets in the query string:
<a href="mailto:info@example.com?subject=%5BAction%20Required%5D%20Review">
Email us
</a>
The [ becomes %5B, the ] becomes %5D, and spaces become %20. Most server side languages and JavaScript provide functions to handle this encoding automatically, such as encodeURIComponent() in JavaScript.
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