HTML Guides for input
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 autocomplete attribute tells the browser whether it can assist the user in filling out a form field, and if so, what type of data is expected. The generic values "on" and "off" control whether the browser should offer autofill suggestions to the user. Since type="hidden" inputs are never displayed and never receive direct user input, these values don't apply — there's no user interaction to assist with.
According to the HTML specification, hidden inputs can have an autocomplete attribute, but only with specific named autofill detail tokens (like "transaction-id" or "cc-number"). These tokens serve a programmatic purpose by providing hints about the semantic meaning of the hidden value, which can be useful for form processing. The generic "on" and "off" values, however, are explicitly disallowed because they only relate to the user-facing autofill behavior.
This validation error matters for standards compliance and can indicate a logical mistake in your markup. If you added autocomplete="off" to a hidden input hoping to prevent the browser from caching or modifying the value, it won't have that effect. Hidden input values are controlled entirely by the server or by JavaScript, not by browser autofill.
How to fix it
- Remove the
autocompleteattribute if it's not needed — this is the most common fix. - Use a specific autofill token if you need to convey semantic meaning about the hidden value (e.g.,
autocomplete="transaction-id"). - Reconsider the input type — if the field genuinely needs autofill behavior controlled, it probably shouldn't be
type="hidden".
Examples
Incorrect: using autocomplete="off" on a hidden input
<form action="/submit" method="post">
<input type="hidden" name="token" value="abc123" autocomplete="off">
<button type="submit">Submit</button>
</form>
Incorrect: using autocomplete="on" on a hidden input
<form action="/submit" method="post">
<input type="hidden" name="session-id" value="xyz789" autocomplete="on">
<button type="submit">Submit</button>
</form>
Correct: removing the autocomplete attribute
<form action="/submit" method="post">
<input type="hidden" name="token" value="abc123">
<button type="submit">Submit</button>
</form>
Correct: using a specific autofill token
If the hidden input carries a value with a well-defined autofill semantic, you can use a named token:
<form action="/checkout" method="post">
<input type="hidden" name="txn" value="TXN-001" autocomplete="transaction-id">
<button type="submit">Complete Purchase</button>
</form>
This is valid because "transaction-id" is a specific autofill detail token recognized by the specification, unlike the generic "on" or "off" values.
Hidden inputs are designed to carry data between the client and server without any user interaction or visual presence. The browser does not render them, screen readers do not announce them, and they are entirely excluded from the accessibility tree. Because aria-* attributes exist solely to convey information to assistive technologies, adding them to an element that assistive technologies cannot perceive is contradictory and meaningless.
The HTML specification explicitly prohibits aria-* attributes on input elements with type="hidden". This restriction exists because WAI-ARIA attributes — such as aria-label, aria-invalid, aria-describedby, aria-required, and all others in the aria-* family — are meant to enhance the accessible representation of interactive or visible elements. A hidden input has no such representation, so these attributes have nowhere to apply.
This issue commonly arises when:
- JavaScript frameworks or templating engines apply
aria-*attributes indiscriminately to all form inputs, regardless of type. - A developer changes an input's type from
"text"to"hidden"but forgets to remove the accessibility attributes that were relevant for the visible version. - Form libraries or validation plugins automatically inject attributes like
aria-invalidonto every input in a form.
To fix the issue, simply remove all aria-* attributes from any input element that has type="hidden". If the aria-* attribute was meaningful on a previously visible input, no replacement is needed — the hidden input doesn't participate in the user experience at all.
Examples
Incorrect: hidden input with aria-invalid
<form action="/submit" method="post">
<input type="hidden" name="referer" value="https://example.com" aria-invalid="false">
<button type="submit">Submit</button>
</form>
Correct: hidden input without aria-* attributes
<form action="/submit" method="post">
<input type="hidden" name="referer" value="https://example.com">
<button type="submit">Submit</button>
</form>
Incorrect: hidden input with multiple aria-* attributes
<form action="/save" method="post">
<input
type="hidden"
name="session_token"
value="abc123"
aria-label="Session token"
aria-required="true"
aria-describedby="token-help">
<button type="submit">Save</button>
</form>
Correct: all aria-* attributes removed
<form action="/save" method="post">
<input type="hidden" name="session_token" value="abc123">
<button type="submit">Save</button>
</form>
Correct: aria-* attributes on a visible input (where they belong)
If the input is meant to be visible and accessible, use an appropriate type value instead of "hidden":
<form action="/login" method="post">
<label for="username">Username</label>
<input
type="text"
id="username"
name="username"
aria-required="true"
aria-invalid="false"
aria-describedby="username-help">
<p id="username-help">Enter your registered email or username.</p>
<button type="submit">Log in</button>
</form>
The label element represents a caption for a form control. There are two ways to associate a label with its control:
- Implicit association — Place the form control directly inside the
labelelement. Noforattribute is needed because the browser automatically pairs the label with the nested control. - Explicit association — Use the
forattribute on thelabel, setting its value to theidof the target form control. The control doesn't need to be nested inside thelabelin this case.
Both methods are valid on their own. The problem occurs when you combine them incorrectly: you nest an input inside a label that has a for attribute, but the input either has no id or has an id that doesn't match the for value. This creates a contradiction — the for attribute points to a specific id, yet the nested input doesn't fulfill that reference. Browsers may handle this inconsistently, and assistive technologies like screen readers could fail to announce the label correctly, harming accessibility.
Why this matters
- Accessibility: Screen readers rely on the
for/idpairing to announce labels for form controls. A mismatched or missingidcan leave the control unlabeled for users who depend on assistive technology. - Standards compliance: The HTML specification requires that when a
forattribute is present, it must reference theidof a labelable element. A mismatch violates this rule. - Browser behavior: Some browsers will fall back to the implicit association when
fordoesn't resolve, but others may prioritize the broken explicit association, leaving the control effectively unlabeled.
How to fix it
You have two options:
- Remove the
forattribute if theinputis already nested inside thelabel. The implicit association is sufficient on its own. - Add or correct the
idon the nestedinputso it matches theforattribute value exactly.
Examples
❌ Nested input with no matching id
The for attribute says "email", but the input has no id at all:
<label for="email">
<input type="email" name="email">
</label>
❌ Nested input with a mismatched id
The for attribute says "email", but the input's id is "user-email":
<label for="email">
<input type="email" name="email" id="user-email">
</label>
✅ Fix by removing the for attribute (implicit association)
Since the input is nested inside the label, the association is automatic:
<label>
<input type="email" name="email">
</label>
✅ Fix by adding a matching id (explicit association)
The for value and the id value are identical:
<label for="email">
<input type="email" name="email" id="email">
</label>
✅ Fix by using explicit association without nesting
If you prefer to keep the for attribute, the input doesn't need to be nested at all:
<label for="email">Email</label>
<input type="email" name="email" id="email">
In most cases, choosing either implicit or explicit association — rather than mixing both — is the simplest way to avoid this error. If you do combine them, just make sure the for value and the id value match exactly.
A control with the disabled attribute is already exposed as disabled, so aria-disabled repeats the same information and can be removed.
The disabled attribute works on <button>, <input>, <select>, <textarea>, <fieldset>, <optgroup> and <option>. Browsers take a disabled control out of the tab order, ignore clicks on it, and tell assistive technologies that it is disabled. Adding aria-disabled="true" says the same thing again. The ARIA in HTML specification advises against it, and the validator reports it as a warning.
You also get this warning when aria-disabled copies the value of disabled, as in disabled="disabled" aria-disabled="disabled", or when an empty aria-disabled="" sits next to a bare disabled. The validator then adds a Bad value error as well, because aria-disabled only accepts true or false.
Remove aria-disabled and keep disabled. If aria-disabled is set to false on an element with disabled, the two attributes contradict each other, and the validator reports that as an error instead.
Sometimes a button should stay focusable while it is unavailable, so keyboard and screen reader users can still reach it and find out why. For that, drop disabled and keep only aria-disabled="true". The validator accepts this, but aria-disabled changes nothing about behavior: the button still responds to clicks and still submits the form, so your script has to ignore those activations while aria-disabled is true.
Invalid example
<form action="/checkout" method="post">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit" disabled aria-disabled="true">Place order</button>
</form>
Valid example
<form action="/checkout" method="post">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<button type="submit" disabled>Place order</button>
</form>
A field with the readonly attribute is already exposed as read-only, so aria-readonly repeats the same information and can be removed.
The readonly attribute on an <input> or <textarea> tells browsers and assistive technologies that the user can't change the value. Adding aria-readonly="true" states the same thing a second time. The ARIA in HTML specification advises against it, and the validator reports it as a warning.
The same warning appears when aria-readonly repeats the exact value of readonly, as in readonly="readonly" aria-readonly="readonly", or an empty aria-readonly="" next to a bare readonly. In those cases the validator also reports a Bad value error, because aria-readonly only accepts true or false.
Remove aria-readonly and keep readonly. aria-readonly is meant for custom widgets, such as an element with role="textbox" or a grid cell, which have no native readonly attribute. If aria-readonly is set to false on a field with readonly, the two attributes contradict each other, and the validator reports that as an error instead.
Invalid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly aria-readonly="true">
Valid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly>
A field with the readonly attribute can't also have aria-readonly="false": remove aria-readonly and let the native attribute set the state.
The readonly attribute on an <input> or <textarea> already tells browsers and assistive technologies that the user can't change the value. aria-readonly="false" claims the opposite, so the field exposes two conflicting states. The ARIA in HTML specification forbids aria-readonly="false" on any element that has readonly, and the validator reports it as an error.
This often comes from a component or template that always outputs aria-readonly from a flag that defaults to false, while readonly is added separately. Decide what the field should be. If users shouldn't edit it, keep readonly and drop aria-readonly. If they should, remove readonly. aria-readonly is meant for custom widgets, such as an element with role="textbox" or a grid cell, which have no native readonly attribute.
Writing aria-readonly="true" next to readonly doesn't trigger this error. That combination is only redundant, and the validator reports it as a warning.
Invalid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly aria-readonly="false">
Valid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly>
A field with the readonly attribute has an aria-readonly value that doesn't match it: remove aria-readonly and keep the native attribute.
aria-readonly only accepts true or false. A value such as TRUE, 1, undefined or readonly is neither, so the validator reports two errors on the same field: a Bad value error for aria-readonly, and this one, because the value differs from what readonly states. Values like these usually come from a template that prints a variable straight into the attribute, for example a boolean that the template language writes as 1 or True.
The readonly attribute already exposes the read-only state to assistive technologies, so the field doesn't need aria-readonly at all. Remove it. If you're building a custom widget that has no native readonly attribute, such as an element with role="textbox", write aria-readonly as exactly true or false.
Other values give different messages. aria-readonly="false" next to readonly is its own error, because it contradicts the native attribute. aria-readonly="true", or a value that exactly repeats the value of readonly (such as readonly="readonly" aria-readonly="readonly"), only gets a warning that the attribute is unnecessary.
Invalid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly aria-readonly="1">
Valid example
<label for="account">Account ID</label>
<input type="text" id="account" name="account" value="AC-1042" readonly>
A field with the required attribute has an aria-required value that doesn't match it: remove aria-required and keep the native attribute.
aria-required only accepts true or false. A value such as yes, 1 or required is neither, so the validator reports two errors on the same field: a Bad value error for aria-required, and this one, because the value differs from what required states. These values usually come from copying the native attribute into the ARIA one, as in aria-required="required", or from a template that prints a boolean as 1 or True.
The required attribute already tells assistive technologies that the field must be filled in, and it turns on the browser's own form validation. The field doesn't need aria-required at all, so remove it. If you're building a custom widget that can't take required, such as an element with role="combobox", write aria-required as exactly true or false.
Other values give different messages. aria-required="false" next to required is its own error, because it contradicts the native attribute. aria-required="true", or a value that exactly repeats the value of required (such as required="required" aria-required="required"), only gets a warning that the attribute is unnecessary.
Invalid example
<label for="email">Email</label>
<input type="email" id="email" name="email" required aria-required="yes">
Valid example
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
The maxlength attribute provides built-in client-side validation that caps the number of characters a user can type into a field. Browsers enforce this by preventing further input once the limit is reached. However, this behavior only makes sense for input types that accept arbitrary text strings. Input types like number, date, range, and checkbox have their own value formats and constraints — a number input's value is controlled by min, max, and step, not by character count.
When you place maxlength on an unsupported input type, browsers will ignore the attribute. This means it provides no actual validation while giving a false sense of security. It also produces invalid HTML, which can cause issues with assistive technologies that may try to interpret the attribute and relay incorrect information to users. Keeping your markup valid ensures predictable behavior across browsers and a better experience for all users.
How to fix it
- Remove
maxlengthfrom any<input>whosetypeis notemail,password,search,tel,text, orurl. - Use the correct constraint attributes for the input type in question. For
numberinputs, useminandmax. Fordateinputs, useminandmaxwith date strings. - If you genuinely need character-length validation, consider whether a text-based input type is more appropriate for your use case, or implement the constraint in JavaScript.
Examples
❌ Invalid: maxlength on a number input
<label for="quantity">Quantity</label>
<input type="number" id="quantity" name="quantity" maxlength="3">
The number type does not support maxlength. Browsers will ignore it, and the HTML is invalid.
✅ Fixed: using min and max for a number input
<label for="quantity">Quantity</label>
<input type="number" id="quantity" name="quantity" min="0" max="999">
If the goal was to limit the value to three digits, min and max are the correct constraints.
❌ Invalid: maxlength on a date input
<label for="start-date">Start date</label>
<input type="date" id="start-date" name="start-date" maxlength="10">
Date inputs have a browser-provided date picker, and their values are always in YYYY-MM-DD format. The maxlength attribute has no effect here.
✅ Fixed: using min and max for a date input
<label for="start-date">Start date</label>
<input type="date" id="start-date" name="start-date" min="2020-01-01" max="2030-12-31">
❌ Invalid: maxlength on a checkbox
<label>
<input type="checkbox" name="agree" maxlength="1"> I agree
</label>
A checkbox is a boolean toggle — character length is meaningless here.
✅ Fixed: removing the invalid attribute
<label>
<input type="checkbox" name="agree"> I agree
</label>
✅ Valid: maxlength on supported text-based types
<label for="username">Username</label>
<input type="text" id="username" name="username" maxlength="30">
<label for="user-email">Email</label>
<input type="email" id="user-email" name="email" maxlength="254">
<label for="user-phone">Phone</label>
<input type="tel" id="user-phone" name="phone" maxlength="15">
<label for="site-url">Website</label>
<input type="url" id="site-url" name="website" maxlength="2048">
<label for="user-pass">Password</label>
<input type="password" id="user-pass" name="password" maxlength="128">
<label for="query">Search</label>
<input type="search" id="query" name="q" maxlength="100">
All six of these input types support maxlength because they accept free-form text where limiting character count is meaningful.
The minlength attribute defines the minimum number of characters (as UTF-16 code units) that a user can enter into a text-based input field. It provides built-in client-side validation without requiring JavaScript. However, it only makes sense on input types where the user is typing free-form text. For input types like number, date, color, range, or checkbox, the concept of a minimum character length doesn't apply — these inputs have their own constrained value formats or use other attributes like min and max for validation.
When the W3C validator encounters minlength on an unsupported input type, it flags the attribute as invalid. Browsers will typically ignore the attribute silently, meaning your intended validation won't actually work. This can lead to a false sense of security where you believe the input is being validated when it isn't.
The minlength value must be a non-negative integer (0 or higher) and must be less than or equal to maxlength if both are specified. The minlength attribute also works on <textarea> elements.
Examples
❌ Incorrect: minlength on a number input
The number input type doesn't support minlength. If you want to enforce a minimum value, use the min attribute instead.
<label for="age">Enter your age</label>
<input type="number" minlength="1" id="age">
✅ Fixed: Using min for number inputs
<label for="age">Enter your age</label>
<input type="number" min="1" id="age">
❌ Incorrect: minlength on a date input
<label for="start-date">Start date</label>
<input type="date" minlength="10" id="start-date">
✅ Fixed: Remove minlength from date inputs
Date inputs have a browser-controlled format, so character length constraints don't apply. Use min and max to constrain the date range.
<label for="start-date">Start date</label>
<input type="date" min="2024-01-01" id="start-date">
✅ Correct: minlength on supported input types
Here are valid uses of minlength across all supported input types:
<label for="username">Username (at least 3 characters)</label>
<input type="text" minlength="3" id="username">
<label for="email">Email</label>
<input type="email" minlength="5" id="email">
<label for="pass">Password (at least 8 characters)</label>
<input type="password" minlength="8" id="pass">
<label for="phone">Phone number</label>
<input type="tel" minlength="7" id="phone">
<label for="query">Search</label>
<input type="search" minlength="2" id="query">
<label for="website">Website URL</label>
<input type="url" minlength="10" id="website">
✅ Correct: minlength with maxlength on a textarea
<label for="bio">Bio (between 10 and 200 characters)</label>
<textarea minlength="10" maxlength="200" id="bio"></textarea>
Quick Reference
| Input Type | Supports minlength? | Alternative |
|---|---|---|
text, email, password, search, tel, url | ✅ Yes | — |
textarea | ✅ Yes | — |
number, range | ❌ No | Use min / max |
date, datetime-local, time, month, week | ❌ No | Use min / max |
checkbox, radio | ❌ No | Use required |
file | ❌ No | Validate with JavaScript |
color, hidden | ❌ No | Not applicable |
The pattern attribute provides a powerful way to add client-side form validation directly in HTML without relying on JavaScript. It accepts a regular expression that the browser uses to validate user input before the form is submitted. However, the HTML specification restricts pattern to input types where the user enters free-form text. Input types like number, date, range, color, and checkbox have their own built-in validation mechanisms (such as min, max, and step), so applying a regex pattern to them is meaningless and invalid.
When you add pattern to an unsupported input type, browsers will simply ignore it. This means you might think you have validation in place when you actually don't, which can lead to unexpected invalid data being submitted. Removing the invalid attribute also keeps your markup clean and standards-compliant, which benefits accessibility tools and future browser behavior.
Why certain types don't support pattern
number— Values are constrained bymin,max, andstep. The browser enforces numeric input natively.date,time,datetime-local,month,week— These use date/time pickers with their own format and range constraints.range— A slider control already constrained bymin,max, andstep.checkbox,radio— These are toggled on/off or selected from a group; a regex pattern doesn't apply.file— File selection is handled by the OS file picker; use theacceptattribute instead.color— Uses a color picker with a fixed hex format.hidden— Not user-editable, so client-side validation is irrelevant.
How to fix it
- Remove the
patternattribute if the input type already provides sufficient validation through its native controls. - Change the input
typeto one of the six supported types (email,password,search,tel,text, orurl) if you genuinely need regex-based validation. - Use alternative attributes like
min,max,step, oracceptthat are designed for the specific input type. - Use JavaScript validation if you need custom validation logic that goes beyond what native attributes offer.
Examples
❌ Incorrect: pattern on a number input
<label for="qty">Quantity (multiples of 5):</label>
<input type="number" id="qty" name="qty" pattern="[0-9]+" min="0" max="100">
The pattern attribute is not allowed on type="number". Since min, max, and step already handle numeric constraints, pattern is unnecessary here.
✅ Correct: using step instead of pattern for number validation
<label for="qty">Quantity (multiples of 5):</label>
<input type="number" id="qty" name="qty" min="0" max="100" step="5">
❌ Incorrect: pattern on a date input
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" pattern="\d{4}-\d{2}-\d{2}">
The date input type already enforces a date format through its native picker, so pattern is invalid here.
✅ Correct: removing pattern from date input
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" min="1900-01-01" max="2025-12-31">
❌ Incorrect: pattern on a checkbox input
<label>
<input type="checkbox" name="agree" pattern=".+"> I agree to the terms
</label>
✅ Correct: using required instead of pattern for checkbox
<label>
<input type="checkbox" name="agree" required> I agree to the terms
</label>
✅ Correct: pattern on a supported text input
<label for="zip">ZIP code:</label>
<input type="text" id="zip" name="zip" pattern="[0-9]{5}" title="Five digit ZIP code" required>
When using pattern on a supported input type, always include a title attribute that describes the expected format. Browsers display the title text as part of the validation error message, helping users understand what input is expected.
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
<label for="price">Price:</label>
<input id="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:
<label for="price">Price:</label>
<input id="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:
<label for="price">Price:</label>
<input id="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
<label for="quantity">Quantity:</label>
<input id="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:
<label for="amount">Amount:</label>
<input id="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 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:
<input type="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:
<div role="combobox" aria-haspopup="listbox" aria-owns="suggestions" aria-expanded="false">
<input type="text" aria-autocomplete="list" aria-controls="suggestions">
</div>
<ul id="suggestions" role="listbox" hidden>
<li role="option" id="opt-1">Apple</li>
<li role="option" id="opt-2">Banana</li>
<li role="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:
<label for="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">
<ul id="fruit-list" role="listbox" hidden>
<li role="option" id="fruit-1">Apple</li>
<li role="option" id="fruit-2">Banana</li>
<li role="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 datetime input type was originally proposed to allow users to enter a date and time along with a timezone. However, no browser ever fully implemented it with a usable UI, and the WHATWG HTML Living Standard officially dropped it. Because datetime is not a recognized value for the type attribute, browsers treat <input type="datetime"> as <input type="text"> — a plain text field with no built-in date or time validation, no native picker, and no semantic meaning for assistive technologies.
This matters for several reasons. From an accessibility standpoint, screen readers and other assistive technologies rely on the correct type attribute to communicate the purpose of an input to users. A fallback to type="text" provides no such context. From a validation and user experience perspective, native date/time input types offer built-in pickers on mobile and desktop browsers, enforce format constraints, and support attributes like min, max, and step — none of which work correctly when the type falls back to plain text. From a standards compliance perspective, using an obsolete type means your HTML is invalid, which can cause issues with automated testing, SEO audits, and quality assurance processes.
How to Fix It
Choose a replacement based on what data you need to collect:
type="datetime-local"— Collects both a date and a time without timezone information. This is the most direct replacement for the olddatetimetype.type="date"— Collects only a date (year, month, day).type="time"— Collects only a time (hours, minutes, optionally seconds).type="text"with a JavaScript widget — Use this approach if you need timezone-aware datetime input, since no native HTML input type currently handles timezones.
The datetime-local input supports useful attributes like min, max, step, value, and name, giving you control over the range and granularity of selectable values. The step attribute is specified in seconds (e.g., step="60" for one-minute intervals, step="1" to allow seconds).
Examples
❌ Invalid: Using the obsolete datetime type
This triggers the W3C validation error:
<form>
<label>
Meeting:
<input type="datetime" name="meeting">
</label>
</form>
✅ Fixed: Using datetime-local
Replace datetime with datetime-local to collect both a date and time:
<form>
<label>
Meeting:
<input type="datetime-local" name="meeting" step="60">
</label>
</form>
✅ Fixed: Using separate date and time inputs
If you prefer to collect the date and time independently, split them into two fields:
<form>
<label>
Meeting date:
<input type="date" name="meeting-date" min="2025-01-01" max="2025-12-31">
</label>
<label>
Meeting time:
<input type="time" name="meeting-time" step="900">
</label>
</form>
In this example, step="900" on the time input sets 15-minute intervals (900 seconds).
✅ Fixed: Using datetime-local with constraints
You can set minimum and maximum allowed values using the standard datetime-local format (YYYY-MM-DDThh:mm):
<form>
<label>
Appointment:
<input
type="datetime-local"
name="appointment"
min="2025-06-01T09:00"
max="2025-06-30T17:00"
step="1800"
required>
</label>
<button type="submit">Book</button>
</form>
This restricts selection to June 2025 during business hours, in 30-minute increments, and makes the field required.
The HTML <input> element's type attribute only accepts a specific set of predefined values defined in the HTML specification. These include values like text, password, email, number, date, datetime-local, checkbox, radio, and others. The value dob — presumably short for "date of birth" — is not among them.
When a browser encounters an invalid type value, it doesn't throw an error or prevent the page from loading. Instead, it treats the input as type="text". This means the input might appear to work, but you lose important benefits: there's no native date picker UI, no built-in date format validation, and no appropriate mobile keyboard. The W3C validator flags this to help you catch the mistake early.
This matters for several reasons:
- Accessibility: Valid input types provide semantic meaning to assistive technologies. A
type="date"input tells screen readers that a date is expected, enabling better guidance for users. - User experience: Native date inputs offer platform-appropriate date pickers on mobile and desktop, reducing input errors.
- Standards compliance: Using invalid attribute values produces unpredictable behavior across browsers and can break future compatibility.
To fix this issue, replace type="dob" with a recognized type. For a date of birth field, type="date" is the most appropriate choice. If you need more control over formatting, you can use type="text" with a JavaScript date picker library or custom validation.
Examples
❌ Invalid: using type="dob"
<label for="dob">Date of Birth:</label>
<input type="dob" id="dob" name="dob">
The browser will treat this as a plain text input, and the W3C validator will report: Bad value "dob" for attribute "type" on element "input".
✅ Fixed: using type="date"
<label for="dob">Date of Birth:</label>
<input type="date" id="dob" name="dob">
This uses the native HTML date input, which provides a built-in date picker in most modern browsers. You can also constrain the date range with min and max attributes:
<label for="dob">Date of Birth:</label>
<input type="date" id="dob" name="dob" min="1900-01-01" max="2025-12-31">
✅ Fixed: using type="text" with a JavaScript date picker
If you need more control over the date picker's appearance or need to support older browsers that lack native date input support, use type="text" and enhance it with JavaScript:
<label for="dob">Date of Birth:</label>
<input type="text" id="dob" name="dob" placeholder="YYYY-MM-DD">
You can then attach a JavaScript date picker library (such as Flatpickr, Pikaday, or a framework-specific component) to this input for a custom date selection experience. When using this approach, make sure to add appropriate aria-* attributes and validation to maintain accessibility.
Valid type values for reference
Here are some commonly used valid type values for the <input> element:
text— plain text inputdate— date picker (year, month, day)datetime-local— date and time picker (no timezone)month— month and year pickernumber— numeric inputemail— email address inputtel— telephone number inputpassword— masked text input
Always choose the type that best matches the data you're collecting. For a date of birth, type="date" is the most semantically correct and user-friendly option.
The autocomplete attribute tells the browser whether and how it should autofill a form field. The HTML living standard defines a specific set of allowed values — an empty string is not among them. When the attribute is present but empty, the browser receives ambiguous instructions: you've explicitly declared the attribute, signaling intent, but provided no actual directive. Different browsers may interpret this inconsistently, some treating it as on, others ignoring it, and others falling back to default behavior.
This matters for several reasons:
- Standards compliance: The WHATWG HTML specification requires
autocompleteto contain either the keywordsonoroff, or one or more valid autofill detail tokens. An empty string satisfies none of these. - Accessibility: Autofill helps users with motor impairments or cognitive disabilities complete forms more quickly and accurately. An ambiguous
autocompletevalue can interfere with assistive technologies that rely on these hints. - User experience: Specific autofill tokens like
email,tel,street-address, andcurrent-passwordallow browsers and password managers to suggest the right data for the right field. Using them correctly makes forms faster and easier to complete.
This issue commonly arises when a framework or template engine outputs autocomplete="" as a default, or when a developer intends to disable autocomplete but leaves the value blank instead of using off.
How to fix it
Choose one of these approaches depending on your intent:
- Remove the attribute if you want default browser behavior (the browser decides whether to autofill).
- Use
onto explicitly allow autofill. - Use
offto explicitly discourage autofill (note: browsers may still autofill for login fields regardless). - Use a specific autofill token to tell the browser exactly what kind of data the field expects. This is the most helpful option for users.
Common autofill tokens include: name, given-name, family-name, email, username, new-password, current-password, tel, street-address, postal-code, country, and cc-number. You can find the full list in the WHATWG autofill specification.
Examples
Incorrect: empty autocomplete value
<input type="text" name="username" autocomplete="">
This triggers the validation error because the attribute is present but contains no valid token.
Correct: remove the attribute entirely
If you have no specific autofill preference, simply omit the attribute:
<input type="text" name="username">
Correct: use on or off
Explicitly enable or disable autofill:
<input type="text" name="username" autocomplete="on">
<input type="text" name="search-query" autocomplete="off">
Correct: use specific autofill tokens
Specific tokens give browsers the best hints for filling in the right data. This is the recommended approach for forms that collect personal information:
<form>
<label for="name">Full name</label>
<input type="text" id="name" name="name" autocomplete="name">
<label for="useremail">Email</label>
<input type="email" id="useremail" name="useremail" autocomplete="email">
<label for="phone">Phone</label>
<input type="tel" id="phone" name="phone" autocomplete="tel">
<label for="pwd">Password</label>
<input type="password" id="pwd" name="pwd" autocomplete="current-password">
<button type="submit">Sign in</button>
</form>
Using precise tokens like current-password and email helps password managers and mobile keyboards provide the most relevant suggestions, improving the experience for all users.
The dir attribute on an <input> element is set to an empty string, and dir only accepts ltr, rtl or auto.
dir sets the direction of the text inside the field. The HTML standard treats an empty or unknown value as if the attribute were missing, so the input takes its direction from its parent and dir="" changes nothing. A bare dir with no value counts as empty too, and gets the same error.
A template that fills dir from the page language can print it empty for left-to-right languages. If the field should follow the page direction, remove the attribute. If people may type in a right-to-left script such as Arabic or Hebrew, which is common in search boxes, use dir="auto" and the browser sets the direction from the first letter typed. Add dirname when the server needs that direction too. The form then submits it as an extra field, such as q.dir=rtl.
Invalid example
<form action="/search">
<input type="search" name="q" aria-label="Search" dir="">
</form>
Valid example
Remove the attribute so the field follows the page direction:
<form action="/search">
<input type="search" name="q" aria-label="Search">
</form>
Or let the field follow the text the user types:
<form action="/search">
<input type="search" name="q" aria-label="Search" dir="auto" dirname="q.dir">
</form>
The max attribute defines the maximum value that is acceptable and valid for the input containing it. When the browser encounters max="", it expects a valid floating-point number as defined by the HTML specification. An empty string cannot be parsed as a number, so the attribute becomes meaningless and triggers a validation error.
This commonly happens when HTML is generated dynamically by a template engine or framework that outputs an empty value for max when no maximum has been configured. It can also occur when a developer adds the attribute as a placeholder intending to fill it in later.
While most browsers will silently ignore an invalid max value, relying on this behavior is problematic for several reasons:
- Standards compliance: The HTML specification requires
maxto be a valid floating-point number when present. - Predictable validation: An empty
maxmeans no client-side maximum constraint is enforced, which may not be the developer's intent. Explicitly removing the attribute makes that intention clear. - Accessibility: Assistive technologies may read the
maxattribute to communicate input constraints to users. An empty value could lead to confusing or undefined behavior.
This error applies to input types that accept numeric-style max values, including number, range, date, datetime-local, month, week, and time.
How to Fix It
- Set a valid numeric value: If you need a maximum constraint, provide a proper floating-point number (e.g.,
max="100"ormax="99.5"). - Remove the attribute: If no maximum is needed, remove the
maxattribute entirely rather than leaving it empty. - Fix dynamic templates: If your HTML is generated from a template, add a conditional check so that
maxis only rendered when a value is actually available.
Examples
❌ Invalid: Empty max attribute
<label for="quantity">Quantity:</label>
<input type="number" id="quantity" name="quantity" max="">
The empty string "" is not a valid floating-point number, so this triggers the validation error.
✅ Fixed: Providing a valid numeric value
<label for="quantity">Quantity:</label>
<input type="number" id="quantity" name="quantity" max="100">
✅ Fixed: Removing the attribute entirely
<label for="quantity">Quantity:</label>
<input type="number" id="quantity" name="quantity">
If no maximum constraint is needed, simply omit the max attribute.
❌ Invalid: Empty max on a date input
<label for="end-date">End date:</label>
<input type="date" id="end-date" name="end-date" max="">
✅ Fixed: Valid date value for max
<label for="end-date">End date:</label>
<input type="date" id="end-date" name="end-date" max="2025-12-31">
For date-related input types, the max value must be in the appropriate date/time format (e.g., YYYY-MM-DD for type="date").
Fixing dynamic templates
If you're generating HTML with a templating language, conditionally include the attribute only when a value exists. For example, in a Jinja2-style template:
<input type="number" id="price" name="price"
{% if max_price %}max="{{ max_price }}"{% endif %}>
This ensures the max attribute is only rendered when max_price has a valid value, avoiding the empty-string problem entirely.
The HTML specification defines maxlength as accepting only a valid non-negative integer — a string of one or more ASCII digits representing a number greater than or equal to zero. An empty string does not satisfy this requirement, so the W3C validator flags it as an error. This commonly happens when a value is dynamically generated by a CMS, template engine, or JavaScript framework and the value ends up blank, or when a developer adds the attribute as a placeholder intending to fill it in later.
Why this matters
While most browsers silently ignore an empty maxlength and impose no character limit, relying on this behavior is problematic for several reasons:
- Standards compliance: Invalid HTML can lead to unpredictable behavior across different browsers and versions. What works today may not work tomorrow.
- Accessibility: Assistive technologies may read or interpret the
maxlengthattribute to communicate input constraints to users. An empty value could cause confusing or incorrect announcements. - Maintainability: An empty
maxlengthis ambiguous — it's unclear whether the developer intended no limit, forgot to set a value, or a bug caused the value to be missing.
How to fix it
You have two options:
- Set a valid non-negative integer: Provide a concrete number that represents the maximum number of characters the user can enter, such as
maxlength="100". - Remove the attribute: If you don't need to enforce a character limit, simply omit
maxlengthaltogether. There is no need to include it with an empty value.
Valid values for maxlength include "0", "1", "255", or any other non-negative whole number. The following are not valid: empty strings (""), negative numbers ("-1"), decimal numbers ("10.5"), or non-numeric strings ("none").
Where maxlength applies
The maxlength attribute is meaningful on text-entry input types: text, search, url, tel, email, password, and also on the textarea element. For non-text input types like number, date, range, or checkbox, the attribute has no effect and should not be used.
Examples
❌ Incorrect: empty string triggers the validation error
<input type="text" name="username" maxlength="">
❌ Incorrect: other invalid values
<input type="text" name="username" maxlength="-1">
<input type="email" name="email" maxlength="none">
<input type="text" name="bio" maxlength="10.5">
✅ Correct: explicit maximum length
<input type="text" name="username" maxlength="30">
✅ Correct: omit the attribute when no limit is needed
<input type="text" name="comment">
✅ Correct: maxlength on a textarea
<textarea name="bio" maxlength="500"></textarea>
✅ Correct: dynamic value with a fallback
If your maxlength value comes from a template or CMS, make sure you either output a valid number or omit the attribute entirely. For example, in a templating language, use conditional logic:
<!-- Only render maxlength if the value is set -->
<input type="text" name="username" maxlength="100">
Rather than rendering an empty attribute like maxlength="", ensure your template skips the attribute when no value is configured.
The min attribute defines the minimum acceptable value for form input types such as number, range, date, time, datetime-local, week, and month. When the browser or the W3C validator encounters min="", it attempts to parse the empty string as a floating point number and fails because the empty string is not a valid representation of any number according to the HTML specification's rules for parsing floating point numbers.
This issue commonly arises when templating engines or server-side code dynamically set the min attribute but output an empty value when no minimum is configured, or when developers add the attribute as a placeholder intending to fill it in later.
Why this matters
- Standards compliance: The HTML specification explicitly requires the
minattribute's value to be a valid floating point number (for numeric types) or a valid date/time string (for date/time types). An empty string satisfies neither requirement. - Unpredictable browser behavior: When browsers encounter an invalid
minvalue, they typically ignore the attribute entirely. This means your intended constraint silently disappears, potentially allowing users to submit out-of-range values. - Accessibility concerns: Assistive technologies may rely on
minandmaxto communicate valid input ranges to users. An invalid value can lead to confusing or missing guidance for screen reader users. - Form validation issues: Built-in browser validation using the Constraint Validation API depends on valid
minvalues. An empty string can cause the browser's native validation to behave inconsistently across different browsers.
How to fix it
You have two straightforward options:
- Provide a valid value: Set
minto the actual minimum number or date/time string you want to enforce. - Remove the attribute: If no minimum constraint is needed, simply omit the
minattribute. The input will then accept any value within its type's natural range.
If your min value comes from dynamic server-side or JavaScript logic, make sure the attribute is only rendered when a valid value is available, rather than outputting an empty string as a fallback.
Examples
❌ Invalid: empty string for min
<input type="number" min="" max="10">
The empty string "" is not a valid floating point number, so this triggers the validation error.
✅ Fixed: provide a valid number
<input type="number" min="0" max="10">
✅ Fixed: remove min if no minimum is needed
<input type="number" max="10">
❌ Invalid: empty min on a range input
<input type="range" min="" max="100" step="5">
✅ Fixed: valid min on a range input
<input type="range" min="0" max="100" step="5">
❌ Invalid: empty min on a date input
<input type="date" min="" max="2025-12-31">
For date inputs, min must be a valid date string in YYYY-MM-DD format — an empty string is equally invalid here.
✅ Fixed: valid min on a date input
<input type="date" min="2025-01-01" max="2025-12-31">
Handling dynamic values in templates
If you're using a templating language and the minimum value might not always exist, conditionally render the attribute rather than outputting an empty value. For example, in a generic template pseudocode:
<!-- Instead of always outputting the attribute: -->
<input type="number" min="" max="10">
<!-- Only include it when a value is available: -->
<input type="number" min="5" max="10">
In practice, use your templating engine's conditional logic (e.g., {% if min_value %}min="{{ min_value }}"{% endif %} in Jinja2, or similar constructs) to ensure min is only present when it holds a valid value.
The name attribute on an <input> element identifies the form control's data when the form is submitted. It acts as the key in the key-value pair sent to the server (e.g., email=user@example.com). When you set name="", the attribute is present but contains an empty string, which the HTML specification considers an invalid value. An empty name means the input's data will be excluded from the form's submission payload in most browsers, making it functionally useless for form processing.
This issue matters for several reasons:
- Form functionality: Inputs with empty names are typically omitted from form data, so the server never receives the user's input.
- Standards compliance: The HTML specification requires that if the
nameattribute is present, its value must not be empty. - JavaScript references: An empty
namemakes it difficult to reference the element using methods likedocument.getElementsByName()orFormData. - Accessibility: Screen readers and assistive technologies may use the
nameattribute to help identify form controls, and an empty value provides no useful information.
Note that the name attribute is not technically required on every <input> element — it's perfectly valid to omit it entirely. For example, inputs used purely for client-side JavaScript interactions without form submission don't need a name. The error specifically arises when the attribute is present but set to an empty string.
To fix the issue, either assign a meaningful name that describes the data the input collects, or remove the name attribute altogether if the input isn't part of a form submission.
Examples
❌ Empty name attribute triggers the error
<form action="/submit" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="">
<button type="submit">Submit</button>
</form>
✅ Providing a meaningful name value
<form action="/submit" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email">
<button type="submit">Submit</button>
</form>
✅ Removing name when the input isn't submitted
If the input is only used for client-side interaction and doesn't need to be part of form data, simply omit the attribute:
<label for="search">Filter results:</label>
<input type="text" id="search">
❌ Multiple inputs with empty names
This pattern sometimes appears when inputs are generated dynamically with placeholder attributes:
<form action="/register" method="post">
<input type="text" name="">
<input type="password" name="">
<button type="submit">Register</button>
</form>
✅ Each input gets a descriptive name
<form action="/register" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<button type="submit">Register</button>
</form>
The autocorrect attribute on <input> elements does not accept "none" as a valid value. The correct value to disable autocorrection is "off".
The autocorrect attribute controls whether the browser should automatically correct text input. It accepts only two values: "on" to enable autocorrection and "off" to disable it. The value "none" is not part of the specification and will fail W3C validation.
This attribute was originally a non-standard Safari extension, but it has since been added to the HTML standard. It applies to <input> elements that accept text input (such as text, search, and url types), <textarea> elements, and elements with contenteditable set.
Note that autocorrect is different from autocomplete, which uses "off" and "on" as well, but also accepts many other token values like "name" or "email". The two attributes control different behaviors: autocorrect handles spelling correction, while autocomplete handles previously entered values and autofill.
Incorrect example
<input type="text" autocorrect="none">
Correct example
<input type="text" autocorrect="off">
The HTML <input> element accepts a specific set of values for its type attribute, as defined by the HTML specification. The value "numeric" is not one of them. When a browser encounters an unrecognized type value, it falls back to type="text", meaning the user gets a plain text field instead of a dedicated numeric input. This fallback behavior means you lose built-in features like increment/decrement spinner controls, numeric keyboard on mobile devices, and native client-side validation that rejects non-numeric entries.
This confusion often arises because of the inputmode="numeric" attribute, which is valid and controls which virtual keyboard appears on mobile devices. The inputmode attribute and the type attribute serve different purposes, and mixing them up leads to this validation error.
Using the correct type="number" value matters for several reasons:
- Accessibility: Screen readers and assistive technologies use the
typeattribute to announce the expected input format to users. - User experience: Browsers display appropriate controls (spinners, numeric keypads) for
type="number"inputs. - Validation: Native form validation automatically checks that the entered value is a valid number, within optional
min/maxbounds and matching an optionalstepvalue. - Standards compliance: Invalid
typevalues cause W3C validation errors and signal potential bugs in your markup.
Examples
Incorrect: using type="numeric"
This triggers the validation error because "numeric" is not a valid type value:
<label for="quantity">Quantity:</label>
<input type="numeric" id="quantity" name="quantity">
The browser will treat this as type="text", so users can type any characters, no spinner controls appear, and no numeric validation occurs.
Correct: using type="number"
Replace "numeric" with "number":
<label for="quantity">Quantity:</label>
<input type="number" id="quantity" name="quantity" min="1" max="10">
This gives you a proper numeric input with optional min, max, and step attributes for constraining the allowed range and increments.
Alternative: using inputmode="numeric" with type="text"
If you need a numeric keyboard on mobile but want more control over the input (for example, accepting values like zip codes or credit card numbers that aren't truly "numbers"), you can use inputmode="numeric" on a text input instead:
<label for="zip">ZIP Code:</label>
<input type="text" inputmode="numeric" pattern="[0-9]{5}" id="zip" name="zip">
Here, type="text" is valid, inputmode="numeric" triggers the numeric keyboard on mobile devices, and the pattern attribute provides validation. This approach is useful when type="number" isn't appropriate — for instance, type="number" strips leading zeros and allows scientific notation like 1e5, which is undesirable for codes and identifiers.
Summary of the fix
| Before (invalid) | After (valid) | Use case |
|---|---|---|
type="numeric" | type="number" | Actual numeric values (quantities, prices, ages) |
type="numeric" | type="text" inputmode="numeric" | Numeric-looking codes (ZIP, PIN, credit card) |
In most cases, simply changing type="numeric" to type="number" is the correct fix. Choose the inputmode approach only when you specifically need a text field with a numeric keyboard.
The search ARIA role is a landmark role, which means it identifies a large, navigable section of a page — specifically, the region that contains the search functionality. Landmark roles help assistive technologies (like screen readers) quickly identify and jump to major sections of a document. Because landmarks describe sections of a page, they belong on container elements that encompass all the parts of the search interface (the label, the input field, the submit button, etc.), not on a single <input> element.
When you place role="search" on an <input>, the validator rejects it because the search role doesn't match the semantics of an input control. An <input> represents a single interactive widget, not a page region. The valid way to indicate that an input field is for search queries is to use <input type="search">, which gives browsers and assistive technologies the correct semantic meaning for that specific control.
Meanwhile, if you want to mark an entire search form as a search landmark, apply role="search" to the <form> element that wraps the search controls. In modern HTML, you can also use the <search> element, which has the implicit search landmark role without needing any ARIA attribute.
How to fix it
- Remove
role="search"from the<input>element. - Change the input's
typeto"search"— this tells browsers and assistive technologies that the field is for search queries. - Apply
role="search"to the wrapping<form>, or use the HTML<search>element as the container.
Examples
❌ Incorrect: role="search" on an <input>
<form>
<label for="query">Search</label>
<input role="search" id="query" name="q">
<button type="submit">Go</button>
</form>
This triggers the validation error because search is not a valid role for <input>.
✅ Correct: type="search" on the input, role="search" on the form
<form role="search">
<label for="query">Search this site</label>
<input type="search" id="query" name="q">
<button type="submit">Go</button>
</form>
Here, role="search" is correctly placed on the <form> element, creating a search landmark. The <input type="search"> conveys the correct semantics for the input field itself.
✅ Correct: Using the <search> element (modern HTML)
<search>
<form>
<label for="query">Search this site</label>
<input type="search" id="query" name="q">
<button type="submit">Go</button>
</form>
</search>
The <search> element has the implicit ARIA role of search, so no explicit role attribute is needed on either the container or the form. This is the most semantic approach in browsers that support it.
✅ Correct: Standalone search input without a landmark
If you simply need a search-styled input without marking up a full landmark region, just use type="search":
<label for="filter">Filter results</label>
<input type="search" id="filter" name="filter">
This gives the input the correct semantics and allows browsers to provide search-specific UI features (such as a clear button) without requiring a landmark role.
The HTML specification defines specific rules about which autocomplete values can be used on which form elements. The street-address token is categorized as a "multiline" autofill field because street addresses often span multiple lines (e.g., "123 Main St\nApt 4B"). Since <input> elements only accept single-line text, the spec prohibits using street-address with them. The <textarea> element, on the other hand, naturally supports multiline content, making it the appropriate host for this token.
This matters for several reasons. First, browsers use autocomplete values to offer autofill suggestions. When the element type doesn't match the expected data format, browsers may not autofill correctly or may ignore the hint entirely. Second, standards compliance ensures consistent behavior across different browsers and assistive technologies. Third, using the correct pairing improves the user experience — users expect their full street address to appear in a field that can actually display it properly.
You have two approaches to fix this:
Use a
<textarea>— If you want the full street address in a single field, switch from<input>to<textarea>. This is the most semantically correct choice when you expect multiline address data.Use line-specific tokens on
<input>elements — If your form design uses separate single-line fields for each part of the address, useaddress-line1,address-line2, andaddress-line3instead. These tokens are explicitly allowed on<input>elements.
Examples
❌ Invalid: street-address on an <input>
<label for="address">Street Address</label>
<input type="text" id="address" name="address" autocomplete="street-address">
This triggers the validation error because street-address requires a multiline control.
✅ Fix: Use a <textarea> with street-address
<label for="address">Street Address</label>
<textarea id="address" name="address" autocomplete="street-address"></textarea>
The <textarea> supports multiline text, so street-address is valid here.
✅ Fix: Use line-specific tokens on <input> elements
<label for="address1">Address Line 1</label>
<input type="text" id="address1" name="address1" autocomplete="address-line1">
<label for="address2">Address Line 2</label>
<input type="text" id="address2" name="address2" autocomplete="address-line2">
The address-line1, address-line2, and address-line3 tokens are single-line autofill fields and are perfectly valid on <input> elements. This approach is common in forms that break the address into separate fields for apartment numbers, building names, or other details.
Summary of allowed pairings
| Token | <input> | <textarea> |
|---|---|---|
street-address | ❌ Not allowed | ✅ Allowed |
address-line1 | ✅ Allowed | ✅ Allowed |
address-line2 | ✅ Allowed | ✅ Allowed |
address-line3 | ✅ Allowed | ✅ Allowed |
Choose the approach that best fits your form layout. If you prefer a single address field, use <textarea> with street-address. If you prefer structured, separate fields, use <input> elements with the appropriate address-line tokens.
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.
1K Weekly
1,000 monthly checks. Cancel anytime.
Get 1K Weekly →Join teams across 40+ countries