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 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.).
The allowfullscreen attribute on an iframe element is a boolean attribute and should not be assigned a value like "1".
Boolean attributes in HTML don't need a value. Their mere presence on an element means "true," and their absence means "false." When you write allowfullscreen="1", the W3C validator flags it because "1" is not a valid value for a boolean attribute.
According to the HTML specification, a boolean attribute can only have three valid forms: the attribute name alone (allowfullscreen), an empty string (allowfullscreen=""), or the attribute's own name as the value (allowfullscreen="allowfullscreen"). Any other value, including "1", "true", or "yes", is technically invalid.
Invalid Example
<iframe
src="https://www.example.com/video"
allowfullscreen="1">
</iframe>
Valid Example
<iframe
src="https://www.example.com/video"
allowfullscreen>
</iframe>
The aria-setsize attribute tells assistive technologies how many items exist in a set of related elements (such as list items or tree items). It is particularly useful when not all items in a set are present in the DOM — for example, in virtualized lists, paginated results, or lazy-loaded content. According to the WAI-ARIA 1.2 specification:
Authors MUST set the value of
aria-setsizeto an integer equal to the number of items in the set. If the total number of items is unknown, authors SHOULD set the value ofaria-setsizeto-1.
This means -1 is a specifically recommended value for cases where the total count of items is indeterminate. The W3C validator's pattern matching expects only non-negative digits, so it rejects the leading - character. This is a known validator bug and does not reflect an actual problem with your HTML.
Why -1 matters for accessibility
When building interfaces with dynamic or partially loaded content, screen readers need to communicate the size of a set to users. If you have a list of search results but don't know the total count, setting aria-setsize="-1" tells assistive technologies that the set size is unknown. Without this, a screen reader might announce incorrect or misleading information about how many items exist.
What you should do
Do not change your markup to work around this validator error. The value -1 is correct and serves an important accessibility purpose. Removing it or replacing it with an arbitrary positive number would degrade the experience for users of assistive technologies.
Examples
Valid usage that triggers the false positive
This markup is correct but will be flagged by the validator:
<ul>
<lirole="option"aria-setsize="-1"aria-posinset="1">Result A</li>
<lirole="option"aria-setsize="-1"aria-posinset="2">Result B</li>
<lirole="option"aria-setsize="-1"aria-posinset="3">Result C</li>
</ul>
Here, the total number of results is unknown (perhaps they are loaded on demand), so aria-setsize="-1" correctly signals this to assistive technologies.
Known set size (no validator error)
When the total number of items is known, use the actual count. This will not trigger the validator error:
<ul>
<lirole="option"aria-setsize="5"aria-posinset="1">Item 1</li>
<lirole="option"aria-setsize="5"aria-posinset="2">Item 2</li>
<lirole="option"aria-setsize="5"aria-posinset="3">Item 3</li>
</ul>
When aria-setsize is not needed
If all items in the set are present in the DOM, you don't need aria-setsize at all — the browser can compute the set size automatically:
<ulrole="listbox">
<lirole="option">Apple</li>
<lirole="option">Banana</li>
<lirole="option">Cherry</li>
</ul>
In summary, if you see this validator error and you're intentionally using aria-setsize="-1" because the total item count is unknown, your code is correct. You can safely ignore this particular warning.
The selected attribute on the <option> element is a boolean attribute and does not accept a value like "1".
Boolean attributes in HTML don't need a value at all. Their mere presence on an element means "true," and their absence means "false." When you write selected="1", the W3C validator flags it because "1" is not a valid value for a boolean attribute.
According to the HTML specification, a boolean attribute can only have three valid forms: the attribute name alone (selected), an empty string (selected=""), or the attribute's own name as the value (selected="selected"). Any other value, including "1", "true", or "yes", is invalid.
Invalid Example
<selectname="color">
<optionvalue="red">Red</option>
<optionvalue="blue"selected="1">Blue</option>
<optionvalue="green">Green</option>
</select>
Valid Example
<selectname="color">
<optionvalue="red">Red</option>
<optionvalue="blue"selected>Blue</option>
<optionvalue="green">Green</option>
</select>
Using just selected without a value is the cleanest and most common approach.
The HTML specification defines the height attribute on <img> as a "valid non-negative integer," which means it must consist only of digits — no units, no percentage signs. When you write height="100%", the validator expects a digit character but encounters %, producing this error. The same rule applies to the width attribute.
This matters for several reasons. First, browsers use the width and height attributes to reserve the correct amount of space for an image before it loads, which prevents layout shifts (a key performance metric known as Cumulative Layout Shift). When the value contains invalid characters like %, browsers may ignore the attribute entirely or interpret it unpredictably, undermining this layout reservation. Second, invalid HTML can cause inconsistent rendering across different browsers and assistive technologies. Third, the percentage-based sizing you likely intended simply isn't supported through HTML attributes — it requires CSS.
The fix depends on what you're trying to achieve:
- Fixed dimensions: Replace the percentage with a plain integer representing the image's intrinsic or desired pixel size (e.g.,
height="300"). - Responsive or percentage-based sizing: Remove the
heightattribute (or set it to the image's intrinsic pixel dimensions) and use CSS to control how the image scales within its container.
It's a good practice to always include both width and height attributes with the image's actual intrinsic dimensions, then use CSS to override the display size. This gives browsers the aspect ratio information they need to reserve space while still allowing flexible layouts.
Examples
Incorrect: percentage value in the height attribute
This triggers the validation error because % is not a valid digit:
<imgsrc="photo.jpg"width="100%"height="100%"alt="A landscape photo">
Fixed: using integer pixel values
Specify the image's intrinsic dimensions as plain numbers:
<imgsrc="photo.jpg"width="800"height="600"alt="A landscape photo">
Fixed: combining HTML attributes with CSS for responsive sizing
Set the intrinsic dimensions in HTML for layout stability, then use CSS to make the image responsive:
<style>
.responsive-img{
width:100%;
height: auto;
}
</style>
<img
src="photo.jpg"
width="800"
height="600"
class="responsive-img"
alt="A landscape photo">
With this approach, the browser knows the image's aspect ratio (800×600) and reserves the appropriate space, while CSS ensures the image scales fluidly to fill its container. The height: auto rule maintains the correct aspect ratio as the width changes.
Fixed: filling a container with CSS only
If you don't know the image's intrinsic dimensions and simply want it to fill a container, you can omit the HTML attributes and rely entirely on CSS:
<style>
.image-container{
width:300px;
height:200px;
}
.image-containerimg{
width:100%;
height:100%;
object-fit: cover;
}
</style>
<divclass="image-container">
<imgsrc="photo.jpg"alt="A landscape photo">
</div>
The object-fit: cover property ensures the image fills the container without distortion, cropping as needed. Note that omitting width and height attributes means the browser cannot reserve space before the image loads, so this approach may cause layout shifts. Where possible, prefer including the intrinsic dimensions as shown in the previous example.
The Accept-CH value is not a valid value for the http-equiv attribute on a <meta> element according to the HTML specification.
The http-equiv attribute on <meta> only accepts a specific set of values defined in the HTML standard. These include content-type, default-style, refresh, x-ua-compatible, and content-security-policy. The Accept-CH header is used for Client Hints, which lets the server request specific information from the browser (like device width or viewport size), but it must be delivered as an actual HTTP response header from the server — not as an HTML <meta> tag.
While some browsers may process Accept-CH in a <meta> tag, this behavior is non-standard and not universally supported. The W3C validator correctly flags it as invalid. To fix this, move the Accept-CH directive to your server's HTTP response headers.
Invalid Example
<head>
<metahttp-equiv="Accept-CH"content="DPR, Viewport-Width, Width">
<title>My Page</title>
</head>
How to Fix
Remove the <meta> tag and configure your server to send the header instead. For example, in an Apache .htaccess file:
Header set Accept-CH "DPR, Viewport-Width, Width"
Or in Nginx:
add_header Accept-CH "DPR, Viewport-Width, Width";
This ensures the Client Hints are delivered through a proper HTTP header, which is both valid and more reliably supported across browsers.
The alert ARIA role is used to communicate important, typically time-sensitive messages to the user. When an element has role="alert", assistive technologies like screen readers will immediately announce its content to the user, interrupting whatever they are currently doing. This makes it ideal for error messages, warnings, or status updates that require immediate attention.
However, not every HTML element can accept every ARIA role. The WHATWG HTML specification and WAI-ARIA in HTML define rules about which roles are allowed on which elements. The <ul> element has an implicit role of list, and the alert role is not among the roles permitted on <ul>. This restriction exists because overriding the semantic meaning of a list element with an alert role creates a conflict — assistive technologies would no longer convey the list structure to users, and the element's children (<li> elements) would lose their meaningful context as list items.
This matters for accessibility and standards compliance. If a screen reader encounters a <ul> with role="alert", the behavior becomes unpredictable. Some screen readers might announce it as an alert but fail to convey the list structure, while others might ignore the role entirely. Users who rely on assistive technology could miss either the alert or the list semantics, both of which may be important.
The fix depends on your intent. If you need to alert the user about content that happens to include a list, wrap the <ul> in a container element (like a <div>) and apply role="alert" to that container. If the content doesn't need to be a list, replace the <ul> with a more appropriate element like <div> or <p>.
Examples
❌ Invalid: role="alert" directly on a <ul>
<ulrole="alert">
<li>Your password must be at least 8 characters.</li>
<li>Your password must contain a number.</li>
</ul>
This triggers the validation error because alert is not a valid role for the <ul> element.
✅ Fixed: Wrapping the list in a <div> with role="alert"
<divrole="alert">
<ul>
<li>Your password must be at least 8 characters.</li>
<li>Your password must contain a number.</li>
</ul>
</div>
Here, the <div> carries the role="alert", so assistive technologies will announce the content immediately. The <ul> retains its native list semantics, and the <li> items are properly conveyed as list items.
✅ Fixed: Using a non-list element when list structure isn't needed
<divrole="alert">
<p>Your session will expire in 2 minutes.</p>
</div>
If your alert content is a simple message rather than a list of items, use a more appropriate element like <p> or <div>.
✅ Fixed: Using aria-live as an alternative for dynamic updates
<divaria-live="assertive"role="alert">
<ul>
<li>Error: Email address is required.</li>
<li>Error: Name field cannot be empty.</li>
</ul>
</div>
The aria-live="assertive" attribute on the wrapper ensures that when the content is dynamically updated, assistive technologies announce the changes immediately. Combined with role="alert" on the wrapper (not the list), this provides robust accessible notifications while preserving list semantics.
Key points to remember
- The
role="alert"attribute cannot be placed on<ul>,<ol>, or<li>elements. - Always apply
role="alert"to a generic container element like<div>or<span>. - If your alert content includes a list, nest the list inside the alert container rather than making the list itself the alert.
- The
alertrole implicitly setsaria-live="assertive"andaria-atomic="true", so you don't need to add those separately when usingrole="alert".
The sandbox attribute applies a strict set of restrictions to content loaded inside an <iframe>. By default, sandboxed iframes cannot run scripts, submit forms, open popups, or access storage. You can selectively lift specific restrictions by adding recognized keywords like allow-scripts, allow-forms, allow-popups, allow-same-origin, and others defined in the WHATWG HTML standard.
The keyword allow-storage-access-by-user-activation was proposed as a way to let sandboxed iframes request access to first-party storage (such as cookies) after a user gesture. However, this keyword was never adopted into the HTML specification. The functionality it aimed to provide is now handled by the Storage Access API, which uses document.requestStorageAccess() and document.hasStorageAccess() in JavaScript. Because the keyword was never standardized, the W3C validator correctly flags it as invalid.
Why this matters
- Standards compliance: Using non-standard keywords means your HTML doesn't conform to the specification, which the validator will flag as an error.
- Browser inconsistency: Since this keyword was experimental and never standardized, browser support is unreliable. Some browsers may silently ignore it, while others may have briefly supported it before removing it.
- False sense of security: Including an unrecognized sandbox keyword doesn't actually enable the behavior you expect. The iframe won't gain storage access just because this keyword is present—the browser simply ignores unknown tokens.
How to fix it
- Remove the invalid keyword from the
sandboxattribute. - Keep any other valid sandbox keywords that your iframe needs.
- Use the Storage Access API in JavaScript within the iframe if you need cross-site storage access. The embedded page must call
document.requestStorageAccess()in response to a user gesture, and thesandboxattribute must includeallow-scriptsandallow-same-originfor this API to work.
Examples
❌ Invalid: using a non-standard sandbox keyword
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin allow-storage-access-by-user-activation">
</iframe>
✅ Valid: removing the non-standard keyword
<iframe
src="https://example.com/widget"
sandbox="allow-scripts allow-same-origin">
</iframe>
The embedded page at https://example.com/widget can then use the Storage Access API in JavaScript:
document.querySelector('#login-button').addEventListener('click',async()=>{
consthasAccess=awaitdocument.hasStorageAccess();
if(!hasAccess){
awaitdocument.requestStorageAccess();
}
// Storage (cookies, etc.) is now accessible
});
✅ Valid: sandbox with other standard keywords
If your iframe doesn't need storage access at all, simply use the standard keywords you require:
<iframe
src="https://example.com/form"
sandbox="allow-scripts allow-forms allow-popups">
</iframe>
Note that for document.requestStorageAccess() to work inside a sandboxed iframe, you must include both allow-scripts (so JavaScript can run) and allow-same-origin (so the iframe retains its origin). Without these, the Storage Access API calls will fail.
The aria-expanded attribute communicates to assistive technologies whether a related grouping element (such as a dropdown menu, accordion panel, or collapsible section) is currently expanded or collapsed. It accepts only three valid values:
"true"— the controlled element is expanded and visible."false"— the controlled element is collapsed and hidden."undefined"— the element has no expandable relationship (this is also the implicit default when the attribute is omitted entirely).
This validation error typically occurs when the attribute is accidentally set to a non-boolean value. A common mistake is writing aria-expanded="aria-expanded", which mimics the old HTML4 pattern for boolean attributes like checked="checked". However, aria-expanded is not a standard HTML boolean attribute — it is an ARIA state attribute that requires an explicit string value of "true" or "false".
Setting an invalid value means assistive technologies like screen readers cannot correctly interpret the state of the control. A screen reader user may not know whether a menu is open or closed, leading to a confusing and inaccessible experience. Browsers may also handle the invalid value unpredictably, potentially treating it as truthy or ignoring it altogether.
How to fix it
- Identify the element with the invalid
aria-expandedvalue. - Replace the value with
"true"if the associated content is currently expanded, or"false"if it is collapsed. - If the button has no expand/collapse relationship at all, remove the
aria-expandedattribute entirely. - Ensure that JavaScript toggling logic updates the attribute to
"true"or"false"— never to any other string.
Examples
❌ Invalid: attribute set to a non-boolean string
<buttonaria-expanded="aria-expanded"aria-controls="menu">
Toggle Menu
</button>
<ulid="menu">
<li>Option 1</li>
<li>Option 2</li>
</ul>
The value "aria-expanded" is not a recognized value and triggers the validation error.
✅ Fixed: attribute set to "false" (collapsed state)
<buttonaria-expanded="false"aria-controls="menu">
Toggle Menu
</button>
<ulid="menu"hidden>
<li>Option 1</li>
<li>Option 2</li>
</ul>
✅ Fixed: attribute set to "true" (expanded state)
<buttonaria-expanded="true"aria-controls="menu">
Toggle Menu
</button>
<ulid="menu">
<li>Option 1</li>
<li>Option 2</li>
</ul>
❌ Invalid: other common incorrect values
<!-- Using "yes" instead of "true" -->
<buttonaria-expanded="yes">Details</button>
<!-- Using "1" instead of "true" -->
<buttonaria-expanded="1">Details</button>
<!-- Empty value -->
<buttonaria-expanded="">Details</button>
All of these are invalid. The only accepted values are "true", "false", and "undefined".
✅ Toggling with JavaScript
When toggling aria-expanded dynamically, make sure the value is always set to the correct string:
<buttonaria-expanded="false"aria-controls="panel"onclick="togglePanel(this)">
Show details
</button>
<divid="panel"hidden>
<p>Additional details here.</p>
</div>
<script>
functiontogglePanel(button){
constexpanded=button.getAttribute("aria-expanded")==="true";
button.setAttribute("aria-expanded",String(!expanded));
constpanel=document.getElementById(button.getAttribute("aria-controls"));
panel.hidden=expanded;
}
</script>
This ensures the attribute always toggles between "true" and "false", keeping the markup valid and the experience accessible for all users.
The W3C HTML validator enforces rules about which ARIA roles can be applied to specific HTML elements. The <section> element carries implicit semantics — it maps to the ARIA region role when it has an accessible name (e.g., via aria-label or aria-labelledby). While article is indeed a valid ARIA role defined in the WAI-ARIA specification, the HTML specification restricts which roles can override a <section> element's native semantics. The allowed roles for <section> include alert, alertdialog, application, contentinfo, dialog, document, feed, log, main, marquee, navigation, none, note, presentation, search, status, tabpanel, and region — but not article.
This restriction exists because HTML already provides the <article> element, which carries the implicit article ARIA role natively. Using role="article" on a <section> creates a confusing mismatch: the element's tag name suggests one semantic meaning while the role attribute declares another. This can confuse assistive technologies like screen readers, which may announce the element inconsistently depending on whether they prioritize the tag name or the explicit role.
The best fix depends on your intent:
- If the content is self-contained and independently meaningful (like a blog post, comment, or news story), replace the
<section>with an<article>element. The<article>element already has the implicitarticlerole, so noroleattribute is needed. - If the content is a thematic grouping within a page, keep the
<section>element and remove theroleattribute. Give it a heading or anaria-labelso it functions as a meaningful landmark. - If you specifically need the
articlerole on a non-semantic element, use a<div>withrole="article"instead, since<div>has no implicit role and allows any ARIA role to be applied.
Examples
Incorrect: role="article" on a <section>
This triggers the validation error because article is not a permitted role for <section>.
<sectionrole="article">
<h2>Breaking news</h2>
<p>Details about the event.</p>
</section>
Correct: use <article> for self-contained content
The <article> element has the implicit article role, making the explicit role attribute unnecessary.
<article>
<h2>Breaking news</h2>
<p>Details about the event.</p>
</article>
Correct: use <section> without a conflicting role
If the content is a thematic grouping rather than a standalone piece, keep <section> and drop the role attribute. Adding an accessible name via aria-labelledby makes it a region landmark.
<sectionaria-labelledby="news-heading">
<h2id="news-heading">Latest news</h2>
<p>Details about the event.</p>
</section>
Correct: use a <div> when you need an explicit article role
In rare cases where you cannot use the <article> element but need the article role, a <div> accepts any valid ARIA role.
<divrole="article">
<h2>Breaking news</h2>
<p>Details about the event.</p>
</div>
Correct: nest <article> inside <section> for grouped articles
If you need both a thematic grouping and individual self-contained items, nest <article> elements inside a <section>.
<sectionaria-labelledby="stories-heading">
<h2id="stories-heading">Top stories</h2>
<article>
<h3>First story</h3>
<p>Story content.</p>
</article>
<article>
<h3>Second story</h3>
<p>Story content.</p>
</article>
</section>
As a general rule, prefer native HTML elements over ARIA roles whenever possible. The <article> element communicates the article role more reliably than any ARIA override, and it works consistently across all browsers and assistive technologies without additional attributes.
The height attribute on an img element contains the string auto" instead of a valid non-negative integer, likely due to a typo with an extra quote.
The height attribute on img elements only accepts non-negative integers representing the image's height in CSS pixels. Values like auto, percentages, or any non-numeric strings are not valid. The extra trailing quote (") in auto" suggests a copy-paste error or a quoting mistake in your markup.
If you want the image height to adjust automatically, simply omit the height attribute altogether, or set height: auto in CSS instead. It's still recommended to include both width and height attributes with actual pixel values to help the browser reserve the correct space and prevent layout shifts (CLS).
Invalid Example
<imgsrc="photo.jpg"alt="A photo"width="600"height="auto">
Fixed Example
Using CSS for automatic height while keeping the HTML attribute for layout stability:
<imgsrc="photo.jpg"alt="A photo"width="600"height="400"style="height: auto;">
Or simply omit the height attribute if you don't know the intrinsic dimensions:
<imgsrc="photo.jpg"alt="A photo"width="600">
The HTML specification defines the height attribute on media elements like <video> as accepting only a valid non-negative integer. This means the attribute value must consist solely of digits (e.g., "360"), with no units, keywords, or other characters. When you write height="auto", the validator expects to find a digit as the first character but encounters the letter "a", which produces the error.
The value "auto" is a valid concept in CSS, where height: auto tells the browser to calculate the element's height automatically based on its intrinsic aspect ratio or content. However, HTML attributes and CSS properties follow different rules. The height HTML attribute is a simple pixel dimension hint — it doesn't understand CSS keywords. Mixing CSS values into HTML attributes is a common mistake, and while browsers may silently ignore the invalid value, it leads to unpredictable behavior: the video may render without any height hint, potentially causing layout shifts as the browser discovers the video's actual dimensions during loading.
Providing a valid height attribute matters for layout stability. When the browser knows the video's dimensions before the media loads, it can reserve the correct amount of space in the page layout, preventing content from jumping around. This improves the user experience and contributes to better Core Web Vitals scores (specifically Cumulative Layout Shift). It also ensures your HTML is standards-compliant and accessible to assistive technologies that may rely on well-formed markup.
How to Fix
You have two approaches:
- Use a numeric value — Replace
"auto"with an integer that represents the video's height in pixels. - Use CSS instead — Remove the
heightattribute from the HTML and applyheight: auto(or any other value) via CSS. This is ideal when you want the video to scale responsively.
If you want the video to maintain its aspect ratio while scaling, the CSS approach is generally preferred for responsive designs. You can combine a width attribute (or CSS width) with CSS height: auto to let the browser calculate the correct height from the video's intrinsic aspect ratio.
Examples
❌ Invalid: Using "auto" in the HTML attribute
<videowidth="640"height="auto"controls>
<sourcesrc="movie.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
This triggers the validation error because "auto" is not a non-negative integer.
✅ Fixed: Using a numeric height value
<videowidth="640"height="360"controls>
<sourcesrc="movie.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
The height attribute is now a valid integer. The browser reserves a 640×360 pixel area for the video before it loads.
✅ Fixed: Using CSS for responsive sizing
<videowidth="640"controlsstyle="height: auto;max-width:100%;">
<sourcesrc="movie.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
Here the height HTML attribute is removed entirely. CSS height: auto ensures the video scales proportionally, and max-width: 100% prevents it from overflowing its container. This is a common pattern for responsive video.
✅ Fixed: Using both attributes with CSS override
<videowidth="640"height="360"controlsstyle="width:100%;height: auto;">
<sourcesrc="movie.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
This approach provides the best of both worlds: the width and height HTML attributes give the browser an aspect ratio hint (preventing layout shifts), while the CSS makes the video responsive. Modern browsers use the attribute values to calculate the correct aspect ratio even when CSS overrides the actual rendered size.
The HTML specification defines the width and height attributes on <img> as accepting only valid non-negative integers — a sequence of one or more digits (0–9) with no letters, units, or symbols. These attributes tell the browser the intrinsic dimensions of the image in pixels, which helps it allocate the correct space in the layout before the image loads, preventing content layout shift (CLS).
When you set height="auto" or width="50%", the validator expects a digit as the first character but instead encounters a letter or symbol, producing the error: "Bad value 'auto' for attribute 'height' on element 'img': Expected a digit but saw 'a' instead."
This matters for several reasons:
- Standards compliance: Browsers may silently ignore invalid attribute values, meaning your intended sizing won't take effect and you'll get default behavior without any visible warning to users.
- Layout stability: Valid
widthandheightattributes allow the browser to calculate the image's aspect ratio before it loads, reserving the correct amount of space and preventing layout shifts. Invalid values defeat this mechanism. - Predictability: Relying on browser error recovery for invalid markup leads to inconsistent behavior across different browsers and versions.
To fix this, you have two options:
- Use plain integers in the
widthandheightattributes to specify pixel dimensions (e.g.,width="600" height="400"). - Use CSS for any non-pixel or dynamic sizing like
auto, percentages,max-width, viewport units, etc.
A best practice is to set the width and height attributes to the image's actual intrinsic pixel dimensions (to preserve aspect ratio and prevent layout shift), then use CSS to control the rendered size responsively.
Examples
Invalid: using "auto" or units in attributes
<!-- "auto" is not a valid integer -->
<imgsrc="photo.jpg"alt="A cat sitting on a windowsill"height="auto"width="auto">
<!-- Percentage is not a valid integer -->
<imgsrc="banner.jpg"alt="Site banner"width="100%">
<!-- Units like "px" are not allowed -->
<imgsrc="icon.png"alt="Settings icon"width="32px"height="32px">
Valid: using plain integers in attributes
<!-- Correct: plain integers representing pixels -->
<imgsrc="photo.jpg"alt="A cat sitting on a windowsill"width="800"height="600">
<imgsrc="icon.png"alt="Settings icon"width="32"height="32">
Valid: using CSS for responsive or dynamic sizing
When you need behavior like auto, percentages, or max-width, use CSS instead:
<!-- Use attributes for intrinsic size, CSS for responsive behavior -->
<img
src="photo.jpg"
alt="A cat sitting on a windowsill"
width="800"
height="600"
style="max-width:100%;height: auto;">
This approach gives you the best of both worlds: the browser knows the image's aspect ratio from the attributes (preventing layout shift), while CSS ensures it scales responsively within its container.
Valid: using a CSS class for reusability
<style>
.responsive-img{
max-width:100%;
height: auto;
}
</style>
<img
src="photo.jpg"
alt="A cat sitting on a windowsill"
width="800"
height="600"
class="responsive-img">
The sizes attribute works together with the srcset attribute to help the browser choose the most appropriate image source for the current viewport and layout. Its value must follow a specific syntax: a comma-separated list where each entry is an optional media condition followed by a CSS length value. The final entry acts as a default and should be a length value without a media condition.
The value "auto" is not recognized as a valid CSS length or source size descriptor by the current HTML specification. While there is a newer sizes="auto" feature being developed for use specifically with loading="lazy" images, it is not yet universally part of the validated standard, and the W3C validator flags it as invalid. When the validator encounters auto, it expects a number (like 100vw or 50px) or a minus sign, but instead finds the letter "a", producing this error.
Why This Matters
- Standards compliance: Using invalid attribute values means your HTML doesn't conform to the specification, which could lead to unpredictable behavior across browsers.
- Responsive image loading: When
sizescontains an invalid value, browsers may fall back to the default value of100vw, which can cause them to download unnecessarily large images, hurting performance. - Accessibility and tooling: Invalid HTML can cause issues with assistive technologies and automated tools that rely on well-formed markup.
How to Fix It
You have several options depending on your use case:
- Specify explicit sizes — Provide a valid source size list that describes how wide the image will be displayed at various viewport widths.
- Use a simple default — Set
sizesto a single CSS length like"100vw"if the image always spans the full viewport width. - Remove
sizesentirely — If you don't usesrcsetwith width descriptors, thesizesattribute is unnecessary and can be removed. - Use
sizes="auto"withloading="lazy"— If you intentionally want the browser to determine sizes automatically for lazy-loaded images, be aware this is a newer feature that the validator may not yet support. You can suppress this specific warning if you've confirmed browser support meets your needs.
Examples
❌ Invalid: Using auto as the sizes value
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="auto"
alt="A scenic mountain view">
This triggers the error because auto is not a valid CSS length or source size descriptor.
✅ Fixed: Using a responsive source size list
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, (max-width: 1000px) 50vw, 33vw"
alt="A scenic mountain view">
This tells the browser: use 100% of the viewport width on screens up to 600px, 50% on screens up to 1000px, and 33% on larger screens. The browser then picks the best image from srcset.
✅ Fixed: Using a simple default size
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w"
sizes="100vw"
alt="A scenic mountain view">
If the image always fills the full viewport width, 100vw is a straightforward valid value.
✅ Fixed: Removing sizes when srcset is not used
<imgsrc="photo.jpg"alt="A scenic mountain view">
If you're not using srcset with width descriptors, the sizes attribute serves no purpose and can be safely removed. Without it, the browser defaults to 100vw when interpreting any srcset width descriptors.
✅ Fixed: Using a fixed pixel width
<img
src="photo.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w"
sizes="400px"
alt="A scenic mountain view">
If the image is always displayed at a fixed width regardless of viewport size, you can specify that width directly as a CSS length value.
The HTML specification defines the width attribute on <video> (and <img>, <canvas>, etc.) as a "valid non-negative integer," which means it must consist only of digits like "640" or "1280". Values like "auto", "100%", or "50vw" are not permitted in the HTML attribute itself — these are CSS concepts, not valid HTML attribute values.
This matters for several reasons. First, browsers use the width and height HTML attributes to reserve the correct amount of space in the layout before the video loads, which prevents content layout shift (CLS). When the value is invalid, the browser may ignore it entirely, leading to layout jumps as the page loads. Second, invalid attributes can cause unpredictable rendering behavior across different browsers. Third, standards compliance ensures your markup is future-proof and works reliably with assistive technologies.
A common reason developers set width="auto" is to make the video responsive. The correct way to achieve this is through CSS rather than through the HTML attribute. You can still set width and height attributes with valid integers to define the video's intrinsic aspect ratio (which helps the browser reserve space), and then override the display size with CSS.
How to Fix
- Replace
"auto"with a valid integer that represents the desired pixel width. - If you need responsive sizing, remove the
widthattribute or keep it for aspect ratio hinting, and use CSS to control the rendered size.
Examples
❌ Invalid: Using "auto" as the width attribute
<videowidth="auto"height="360"controls>
<sourcesrc="video.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
This triggers the error because "auto" is not a non-negative integer.
✅ Fixed: Specifying a valid pixel value
<videowidth="640"height="360"controls>
<sourcesrc="video.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
The width and height attributes use plain integers — no units, no keywords. The browser interprets these as pixels.
✅ Fixed: Responsive video using CSS
If you want the video to scale fluidly with its container, use CSS instead of the HTML attribute:
<style>
.responsive-video{
width:100%;
height: auto;
}
</style>
<videoclass="responsive-video"controls>
<sourcesrc="video.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
In CSS, width: 100% and height: auto are perfectly valid and will make the video scale to fill its container while maintaining its aspect ratio.
✅ Best practice: Combine HTML attributes with CSS
For the best of both worlds — layout stability and responsive sizing — provide width and height attributes for aspect ratio hinting, then override with CSS:
<style>
.responsive-video{
max-width:100%;
height: auto;
}
</style>
<videoclass="responsive-video"width="640"height="360"controls>
<sourcesrc="video.mp4"type="video/mp4">
Your browser does not support the video tag.
</video>
Here, the width="640" and height="360" attributes tell the browser the video's intrinsic 16:9 aspect ratio, so it can reserve the right amount of space before the video loads. The CSS max-width: 100% ensures the video never exceeds its container, and height: auto keeps the aspect ratio intact. This approach minimizes layout shift while remaining fully responsive.
The auto keyword is not a valid value for the sizes attribute on a <source> element according to the W3C HTML validator.
This error typically appears when sizes="auto, ..." is used, often copied from lazy-loading patterns. The sizes attribute expects a comma-separated list of entries, where each entry is an optional media condition followed by a CSS length value. The value auto is not a recognized CSS length or source size descriptor, so the validator flags it whether used alone or combined with other entries.
To fix this, remove auto from the sizes attribute and use explicit media conditions with CSS length values instead.
How to fix
This source triggers the validation error:
<picture>
<source
srcset="image-640.jpg 640w, image-1024.jpg 1024w"
sizes="auto, (max-width: 1024px) 100vw, 1024px">
<imgsrc="image-1024.jpg"alt="Example"loading="lazy">
</picture>
Remove auto and use explicit media conditions:
<picture>
<source
srcset="image-640.jpg 640w, image-1024.jpg 1024w"
sizes="(max-width: 1024px) 100vw, 1024px">
<imgsrc="image-1024.jpg"alt="Example"loading="lazy">
</picture>
The itemscope attribute is part of the HTML Microdata specification, used to define the scope of structured data on a page. It works alongside itemtype and itemprop to provide machine-readable metadata about your content, which search engines and other tools can use to better understand your pages.
In HTML, boolean attributes follow a specific rule: their mere presence on an element represents a true state, and their absence represents false. Unlike JavaScript or other programming languages where you might write itemscope="true" or itemscope="false", HTML boolean attributes do not work this way. The only valid forms for a boolean attribute are:
- The attribute name alone:
itemscope - The attribute with an empty value:
itemscope="" - The attribute with its own name as the value:
itemscope="itemscope"
Assigning any other value — including "true" or "false" — is invalid HTML. This is especially confusing because itemscope="false" does not disable the attribute. Since the attribute is still present on the element, the browser treats it as active. This can lead to incorrect structured data being generated, which may cause search engines to misinterpret your content.
This issue matters for several reasons:
- Standards compliance: Invalid attribute values violate the HTML specification, causing W3C validation errors.
- Structured data accuracy: Incorrect microdata markup can result in search engines misreading your page content, potentially affecting rich search results.
- Developer intent: Writing
itemscope="false"suggests you want to disable the attribute, but it actually does the opposite — the attribute remains active.
To fix this, simply use the bare attribute name when you want it enabled, or remove it entirely when you don't.
Examples
Incorrect: assigning "true" to itemscope
<htmlitemscope="true"itemtype="https://schema.org/WebPage">
<!-- ... -->
</html>
Incorrect: assigning "false" to itemscope
This does not disable itemscope — the attribute is still present, so the browser treats it as active.
<divitemscope="false"itemtype="https://schema.org/Product">
<spanitemprop="name">Widget</span>
</div>
Correct: using the bare attribute
<htmlitemscopeitemtype="https://schema.org/WebPage">
<!-- ... -->
</html>
Correct: using an empty string value
This is an equally valid way to specify a boolean attribute, though the bare form is more common and readable.
<divitemscope=""itemtype="https://schema.org/Product">
<spanitemprop="name">Widget</span>
</div>
Correct: removing the attribute entirely
If you don't need itemscope on the element, simply omit it.
<htmllang="en">
<head>
<title>My Page</title>
</head>
<body>
<p>No microdata here.</p>
</body>
</html>
Correct: a complete example with microdata
<!DOCTYPE html>
<htmllang="en"itemscopeitemtype="https://schema.org/WebPage">
<head>
<title>My Product</title>
</head>
<body>
<divitemscopeitemtype="https://schema.org/Product">
<h1itemprop="name">Super Widget</h1>
<pitemprop="description">The best widget money can buy.</p>
</div>
</body>
</html>
This same rule applies to all HTML boolean attributes, such as hidden, disabled, checked, required, readonly, autoplay, and defer. None of them accept "true" or "false" as values — they are either present or absent.
The ARIA in HTML specification defines which roles are allowed on each HTML element. Heading elements (<h1>–<h6>) have an implicit role of heading, and the set of roles they can be explicitly assigned is limited. The button role is not among them, so applying role="button" directly to a heading element is invalid.
This matters for several reasons. First, headings play a critical role in document structure and accessibility — screen reader users rely on headings to navigate and understand the page hierarchy. Assigning role="button" to a heading overrides its semantic meaning, which confuses assistive technologies. Second, browsers and screen readers may handle conflicting semantics unpredictably, leading to an inconsistent experience for users. Third, it violates the W3C HTML specification, which means your markup won't pass validation.
Why This Combination Is Problematic
When you apply role="button" to an element, assistive technologies treat it as an interactive button. This completely replaces the element's native heading semantics. Users who navigate by headings would no longer find that heading in their list, and users who navigate by interactive controls would encounter a "button" that may not behave like one (lacking keyboard support, focus management, etc.).
If you genuinely need something that looks like a heading but acts as a button, there are valid approaches to achieve this without breaking semantics.
How to Fix It
There are several strategies depending on your intent:
Use a
<button>element styled as a heading. This is often the cleanest approach when the primary purpose is interactivity. You can style the button with CSS to match your heading appearance.Wrap the heading in a
<div>withrole="button". This preserves the heading in the document outline while making the wrapper interactive. However, be aware that thebuttonrole appliesrole="presentation"to all descendant elements, meaning assistive technologies will strip the heading semantics from the<h2>inside it. The text content remains accessible, but it won't be recognized as a heading.Place a
<button>inside the heading. This keeps the heading semantics intact for document structure while making the text inside it interactive. This pattern is commonly used for accordion-style components and is the approach recommended by the WAI-ARIA Authoring Practices.
Examples
❌ Invalid: role="button" on a heading
<h2role="button">Toggle Section</h2>
This triggers the validation error because button is not an allowed role for heading elements.
✅ Fix: Use a <button> styled as a heading
<buttontype="button"class="heading-style">Toggle Section</button>
.heading-style{
font-size:1.5em;
font-weight: bold;
background: none;
border: none;
cursor: pointer;
}
✅ Fix: Wrap the heading in a container with role="button"
<divrole="button"tabindex="0">
<h2>Toggle Section</h2>
</div>
Note that this approach causes the <h2> to lose its heading semantics for assistive technologies, since the button role does not support semantic children. Also remember to add tabindex="0" so the element is keyboard-focusable, and implement keydown handlers for Enter and Space to replicate native button behavior.
✅ Fix: Place a <button> inside the heading (recommended for accordions)
<h2>
<buttontype="button"aria-expanded="false">
Toggle Section
</button>
</h2>
This is the most robust pattern. The heading remains in the document outline, and the button inside it is fully interactive with built-in keyboard support. Screen reader users can find it both when navigating by headings and when navigating by interactive elements. The aria-expanded attribute communicates whether the associated section is open or closed.
The <li> element has an implicit ARIA role of listitem, and the WHATWG HTML specification restricts which roles can be applied to it. The button role is not among the roles permitted on <li>. When you set role="button" on a <li>, you're telling assistive technologies that the element is a button, but the browser and the spec still recognize it as a list item. This creates a semantic conflict that can confuse screen readers and other assistive tools, leading to a degraded experience for users who rely on them.
Beyond the validation error, there are practical accessibility concerns. A real <button> element comes with built-in keyboard support (it's focusable and activatable with Enter or Space), whereas a <li> with role="button" lacks these behaviors by default. You would need to manually add tabindex, keyboard event handlers, and focus styling—effectively recreating what <button> gives you for free. This is error-prone and violates the ARIA principle of preferring native HTML elements over ARIA role overrides.
How to Fix
There are several approaches depending on your use case:
- Place a
<button>inside each<li>— This is the best approach when you have a list of actions, as it preserves list semantics while providing proper button functionality. - Use
<button>elements directly — If the items aren't truly a list, drop the<ul>/<li>structure and use<button>elements instead. - Use a
<div>or<span>withrole="button"— If you cannot use a native<button>for some reason, these elements accept thebuttonrole. You'll also need to addtabindex="0"and keyboard event handling yourself.
Examples
❌ Invalid: role="button" on <li> elements
<ul>
<lirole="button">Copy</li>
<lirole="button">Paste</li>
<lirole="button">Delete</li>
</ul>
This triggers the validation error because <li> does not permit the button role.
✅ Fixed: Using <button> elements inside <li>
<ul>
<li><buttontype="button">Copy</button></li>
<li><buttontype="button">Paste</button></li>
<li><buttontype="button">Delete</button></li>
</ul>
This preserves the list structure while providing proper, accessible button behavior with no extra work.
✅ Fixed: Using standalone <button> elements
If the list structure isn't meaningful, remove it entirely:
<div>
<buttontype="button">Copy</button>
<buttontype="button">Paste</button>
<buttontype="button">Delete</button>
</div>
✅ Fixed: Using a toolbar pattern
For a group of related actions, the ARIA toolbar pattern is a great fit:
<divrole="toolbar"aria-label="Text actions">
<buttontype="button">Copy</button>
<buttontype="button">Paste</button>
<buttontype="button">Delete</button>
</div>
✅ Fixed: Using role="button" on a permitted element
If you truly cannot use a native <button>, a <div> or <span> can accept the button role. Note that you must manually handle focus and keyboard interaction:
<divrole="button"tabindex="0">Copy</div>
However, this approach is almost always inferior to using a native <button> and should only be used as a last resort. Native elements provide keyboard behavior, form integration, and consistent styling hooks that are difficult to replicate reliably.
The http-equiv attribute on the <meta> element is designed to simulate certain HTTP response headers when a server isn't configured to send them directly. However, the HTML specification only permits a limited set of values for this attribute. According to the WHATWG HTML living standard, the valid http-equiv values are:
content-type— an alternative way to declare character encodingdefault-style— sets the preferred stylesheetrefresh— redirects or reloads the page after a delayx-ua-compatible— specifies document compatibility mode for Internet Explorercontent-security-policy— declares a content security policy
Using Cache-Control as an http-equiv value is a pattern that originated in early web development, when some browsers attempted to honor cache directives set through <meta> tags. In practice, modern browsers ignore <meta http-equiv="Cache-Control"> entirely. Caching behavior is determined by actual HTTP response headers sent by the server, not by <meta> tags in the document body. This means the tag not only triggers a validation error but also has no practical effect — it gives a false sense of control over caching while doing nothing.
This matters for several reasons. Invalid HTML can cause unexpected behavior in browsers, particularly edge cases with older or less common user agents. It also undermines confidence in your markup — if a validator flags issues, it becomes harder to spot genuinely important errors. Additionally, relying on a non-functional tag for caching can lead to real problems if developers assume caching is being handled when it isn't.
The correct approach is to configure cache-control headers on your web server or application layer. Every major web server and framework provides a straightforward way to set Cache-Control HTTP headers.
For Apache, you can add this to your .htaccess or server configuration:
Header set Cache-Control "no-cache, no-store, must-revalidate"
For Nginx, use:
add_header Cache-Control "no-cache, no-store, must-revalidate";
In a Node.js/Express application:
res.set('Cache-Control','no-cache, no-store, must-revalidate');
Examples
Invalid: Using Cache-Control as an http-equiv value
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
<metahttp-equiv="Cache-Control"content="no-cache">
<metahttp-equiv="Pragma"content="no-cache">
<metahttp-equiv="Expires"content="0">
</head>
<body>
<p>This page attempts to control caching via meta tags.</p>
</body>
</html>
In this example, all three <meta> tags are problematic. Cache-Control and Pragma are not valid http-equiv values. While Expires was historically used, it is also not in the current list of conforming values in the WHATWG specification.
Fixed: Removing invalid <meta> tags
<!DOCTYPE html>
<htmllang="en">
<head>
<title>My Page</title>
<!-- Cache-Control should be set via server HTTP headers -->
</head>
<body>
<p>Caching is now properly handled server-side.</p>
</body>
</html>
The invalid <meta> tags are removed entirely. Cache behavior is configured on the server, where it actually takes effect.
Valid: Using a permitted http-equiv value
<!DOCTYPE html>
<htmllang="en">
<head>
<title>Redirecting</title>
<metahttp-equiv="refresh"content="5;url=https://example.com">
</head>
<body>
<p>You will be redirected in 5 seconds.</p>
</body>
</html>
This example uses refresh, which is a valid http-equiv value. It demonstrates what the attribute is actually designed for — a small set of well-defined, browser-supported directives.
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 http-equiv attribute on <meta> elements acts as a pragma directive, simulating the effect of an HTTP response header. The HTML specification defines a strict list of allowed values, including content-type, default-style, refresh, x-ua-compatible, and content-security-policy. Any value not on this list — such as cleartype — is considered invalid and will trigger a validation error.
The <meta http-equiv="cleartype" content="on"> tag was a proprietary Microsoft extension designed to activate ClearType text smoothing in Internet Explorer Mobile 6 and 7 on Windows Phone. ClearType is a sub-pixel rendering technology that improves the readability of text on LCD screens. Since these browsers are long obsolete, this meta tag serves no practical purpose today. No modern browser recognizes or acts on it.
Keeping invalid meta tags in your HTML has several downsides:
- Standards compliance: It produces W3C validation errors, which can mask other, more important issues in your markup.
- Code cleanliness: Dead code clutters your document head and confuses developers who may not know its history.
- No functional benefit: Since no current browser or rendering engine uses this directive, it provides zero value.
The fix is straightforward: remove the <meta http-equiv="cleartype"> tag. If your project requires smooth font rendering on modern browsers, CSS properties like font-smooth (non-standard) or -webkit-font-smoothing and -moz-osx-font-smoothing can be used instead, though these are also non-standard and should be used with care.
Examples
❌ Invalid: Using cleartype as an http-equiv value
<head>
<metacharset="utf-8">
<metahttp-equiv="cleartype"content="on">
<title>My Page</title>
</head>
This triggers the validation error because cleartype is not a valid value for http-equiv.
✅ Fixed: Remove the invalid meta tag
<head>
<metacharset="utf-8">
<title>My Page</title>
</head>
Simply removing the tag resolves the error with no loss of functionality in modern browsers.
✅ Alternative: Use CSS for font smoothing if needed
If you want to influence text rendering, use CSS instead of a non-standard meta tag:
body{
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
Note that these CSS properties are themselves non-standard and primarily affect macOS and iOS rendering. On Windows, modern browsers already apply ClearType or DirectWrite smoothing automatically without any developer intervention.
The combobox role represents a composite widget that combines a text input with a popup (typically a listbox) that helps the user set the value of the input. It's a common pattern seen in autocomplete fields, search suggestions, and dropdown selects with type-ahead functionality.
The reason the validator rejects role="combobox" on <input> is rooted in how the HTML specification defines allowed ARIA roles for each element. An <input type="text"> already carries an implicit role of textbox, and the spec only permits a limited set of explicit roles on it (such as combobox in ARIA 1.2 contexts, but W3C HTML validation may still flag this depending on the validator's conformance rules). When the validator encounters a role that isn't in its allowed list for that element, it raises this error.
Why This Matters
- Standards compliance: Using roles outside the permitted set for an element violates the HTML specification, which can lead to unpredictable behavior across browsers and assistive technologies.
- Accessibility: Assistive technologies like screen readers rely on correct role assignments to convey the widget's purpose. A misapplied role can confuse users who depend on these tools, making the combobox harder to navigate or understand.
- Browser interop: Browsers may handle conflicting implicit and explicit roles inconsistently, leading to different experiences across platforms.
How to Fix It
There are two main approaches depending on which ARIA pattern you follow:
Approach 1: ARIA 1.1 Pattern (Container as Combobox)
In the ARIA 1.1 combobox pattern, the role="combobox" is placed on a container element (like a <div>) that wraps the <input>. This is the approach most likely to pass W3C validation without issues.
Approach 2: ARIA 1.2 Pattern (Input as Combobox)
ARIA 1.2 moved the combobox role directly onto the <input> element itself, which is a simpler and more widely adopted pattern in practice. However, the W3C HTML Validator may still flag this as an error if its conformance rules haven't been updated to reflect ARIA 1.2. If passing validation is a strict requirement, use Approach 1.
Examples
❌ Incorrect: role="combobox" directly on <input>
This triggers the validation error:
<inputtype="text"role="combobox"aria-autocomplete="list">
✅ Correct: ARIA 1.1 pattern with container element
The role="combobox" is placed on the wrapping <div>, and the <input> inside it handles text entry:
<divrole="combobox"aria-haspopup="listbox"aria-owns="suggestions"aria-expanded="false">
<inputtype="text"aria-autocomplete="list"aria-controls="suggestions">
</div>
<ulid="suggestions"role="listbox"hidden>
<lirole="option"id="opt-1">Apple</li>
<lirole="option"id="opt-2">Banana</li>
<lirole="option"id="opt-3">Cherry</li>
</ul>
Key attributes explained:
role="combobox"on the<div>defines the overall widget for assistive technologies.aria-haspopup="listbox"tells screen readers that this widget has a popup of typelistbox.aria-owns="suggestions"establishes an ownership relationship between the combobox and the listbox, even if they aren't parent-child in the DOM.aria-expanded="false"indicates whether the popup is currently visible. Update this to"true"via JavaScript when the list is shown.aria-autocomplete="list"on the<input>signals that suggestions will be presented in a list as the user types.aria-controls="suggestions"on the<input>links it to the listbox it controls.
✅ Correct: ARIA 1.2 pattern using <input> with role="combobox" (if validation is not strict)
If your project follows ARIA 1.2 and you can tolerate or suppress the validation warning, this pattern is widely supported by modern browsers and screen readers:
<labelfor="fruit">Choose a fruit</label>
<input
id="fruit"
type="text"
role="combobox"
aria-autocomplete="list"
aria-expanded="false"
aria-controls="fruit-list"
aria-haspopup="listbox">
<ulid="fruit-list"role="listbox"hidden>
<lirole="option"id="fruit-1">Apple</li>
<lirole="option"id="fruit-2">Banana</li>
<lirole="option"id="fruit-3">Cherry</li>
</ul>
This is the pattern recommended by the WAI-ARIA Authoring Practices Guide and is the most commonly implemented in modern component libraries. If validation compliance is required, wrap the input in a container and move the role="combobox" there as shown in Approach 1.
Whichever approach you choose, remember that ARIA attributes alone don't create behavior — you'll need JavaScript to toggle aria-expanded, manage focus, handle keyboard navigation, and update aria-activedescendant as the user moves through options.
The autocomplete attribute helps browsers automatically fill in form fields with previously saved user data. The HTML specification defines a strict set of valid autofill field names, and "company" is not among them. While "company" might seem like an intuitive choice, the spec uses "organization" to represent a company name, business name, or other organizational name associated with the person or address in the form.
Using an invalid autocomplete value means browsers won't recognize the field's purpose and cannot offer relevant autofill suggestions. This degrades the user experience — especially on mobile devices where autofill significantly speeds up form completion. It also impacts accessibility, as assistive technologies may rely on valid autocomplete tokens to help users understand and complete forms efficiently.
The full list of valid autofill field names is defined in the WHATWG HTML Living Standard. Some commonly used values include "name", "email", "tel", "street-address", "postal-code", "country", and "organization". When choosing a value, always refer to the specification rather than guessing a name that seems logical.
Examples
❌ Invalid: using "company" as an autocomplete value
<labelfor="company">Company Name</label>
<inputtype="text"id="company"name="company"autocomplete="company">
This triggers the validation error because "company" is not a recognized autofill field name.
✅ Valid: using "organization" instead
<labelfor="company">Company Name</label>
<inputtype="text"id="company"name="company"autocomplete="organization">
The value "organization" is the spec-defined autofill field name for "the company, organization, institution, or other entity associated with the person, address, or contact information in the other fields associated with this field."
✅ Valid: using "organization" with a section and purpose
You can combine "organization" with other valid tokens for more specificity:
<labelfor="work-org">Employer</label>
<inputtype="text"id="work-org"name="employer"autocomplete="section-work organization">
This tells the browser that the field is for an organization name within a specific named section of the form, which is useful when a form collects information about multiple entities.
Common Autofill Field Names for Business Forms
Here are some valid autocomplete values you might use alongside "organization" in a business-related form:
"organization"— company or organization name"organization-title"— job title (e.g., "Software Engineer", "CEO")"name"— full name of the contact person"email"— email address"tel"— telephone number"street-address"— full street address
Using the correct values ensures browsers can provide meaningful autofill suggestions, making your forms faster and easier to complete.
The WAI-ARIA specification defines a strict set of valid role values, and "complimentary" is not among them. This is a straightforward typo — "complimentary" (meaning "expressing praise or given free of charge") versus "complementary" (meaning "serving to complete or enhance something"). When a browser or assistive technology encounters an unrecognized role value, it ignores it. This means screen reader users lose the semantic meaning that the <aside> element would normally convey, making it harder for them to understand the page structure and navigate effectively.
The <aside> element already carries an implicit ARIA role of complementary as defined by the HTML specification. This means assistive technologies automatically treat <aside> as complementary content without any explicit role attribute. Adding role="complementary" to an <aside> is redundant. The simplest and best fix is to remove the misspelled role attribute and let the element's native semantics do the work.
If you have a specific reason to explicitly set the role — for example, when overriding it with a different valid role — make sure the value is spelled correctly and is an appropriate role for the element.
Examples
❌ Incorrect: misspelled role value
<asiderole="complimentary">
<h2>Related Articles</h2>
<ul>
<li><ahref="/guide-one">Getting started guide</a></li>
<li><ahref="/guide-two">Advanced techniques</a></li>
</ul>
</aside>
The value "complimentary" is not a valid ARIA role. Assistive technologies will ignore it, and the element loses its semantic meaning.
✅ Correct: remove the redundant role
<aside>
<h2>Related Articles</h2>
<ul>
<li><ahref="/guide-one">Getting started guide</a></li>
<li><ahref="/guide-two">Advanced techniques</a></li>
</ul>
</aside>
The <aside> element already implies role="complementary", so no explicit role is needed. This is the recommended approach.
✅ Correct: explicitly set the properly spelled role
<asiderole="complementary">
<h2>Related Articles</h2>
<ul>
<li><ahref="/guide-one">Getting started guide</a></li>
<li><ahref="/guide-two">Advanced techniques</a></li>
</ul>
</aside>
This is valid but redundant. It may be appropriate in rare cases where you want to be explicit for clarity or to work around edge cases with certain assistive technologies.
Quick reference for similar typos
| Incorrect (typo) | Correct | Implicit on element |
|---|---|---|
complimentary | complementary | <aside> |
navagation | navigation | <nav> |
presentaion | presentation | (none) |
Always double-check role values against the WAI-ARIA role definitions to ensure they are valid. When an HTML element already provides the semantics you need, prefer using the element without an explicit role — this follows the first rule of ARIA: "If you can use a native HTML element with the semantics and behavior you require, do so."
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