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 autocomplete attribute if it’s not needed — this is the most common fix.
- Use a specific autofill token if you need to convey semantic meaning about the hidden value (e.g., autocomplete="transaction-id").
- Reconsider the input type — if the field genuinely needs autofill behavior controlled, it probably shouldn’t be type="hidden".
Examples
Incorrect: using autocomplete="off" on a hidden input
<form action="/submit" method="post">
<input type="hidden" name="token" value="abc123" autocomplete="off">
<button type="submit">Submit</button>
</form>
Incorrect: using autocomplete="on" on a hidden input
<form action="/submit" method="post">
<input type="hidden" name="session-id" value="xyz789" autocomplete="on">
<button type="submit">Submit</button>
</form>
Correct: removing the autocomplete attribute
<form action="/submit" method="post">
<input type="hidden" name="token" value="abc123">
<button type="submit">Submit</button>
</form>
Correct: using a specific autofill token
If the hidden input carries a value with a well-defined autofill semantic, you can use a named token:
<form action="/checkout" method="post">
<input type="hidden" name="txn" value="TXN-001" autocomplete="transaction-id">
<button type="submit">Complete Purchase</button>
</form>
This is valid because "transaction-id" is a specific autofill detail token recognized by the specification, unlike the generic "on" or "off" values.
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-invalid onto 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 label element. No for attribute is needed because the browser automatically pairs the label with the nested control.
- Explicit association — Use the for attribute on the label, setting its value to the id of the target form control. The control doesn’t need to be nested inside the label in 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/id pairing to announce labels for form controls. A mismatched or missing id can leave the control unlabeled for users who depend on assistive technology.
- Standards compliance: The HTML specification requires that when a for attribute is present, it must reference the id of a labelable element. A mismatch violates this rule.
- Browser behavior: Some browsers will fall back to the implicit association when for doesn’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 for attribute if the input is already nested inside the label. The implicit association is sufficient on its own.
- Add or correct the id on the nested input so it matches the for attribute 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">
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">
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>
Email
<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">
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.
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 maxlength from any <input> whose type is not email, password, search, tel, text, or url.
- Use the correct constraint attributes for the input type in question. For number inputs, use min and max. For date inputs, use min and max with 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 by min, max, and step. 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 by min, max, and step.
- 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 the accept attribute 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 pattern attribute if the input type already provides sufficient validation through its native controls.
- Change the input type to one of the six supported types (email, password, search, tel, text, or url) if you genuinely need regex-based validation.
- Use alternative attributes like min, max, step, or accept that 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 step to 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 step values 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 step to 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., 1 for type="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 type listbox.
- 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 old datetime type.
- 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 input
- date — date picker (year, month, day)
- datetime-local — date and time picker (no timezone)
- month — month and year picker
- number — numeric input
- email — email address input
- tel — telephone number input
- password — 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 autocomplete to contain either the keywords on or off, 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 autocomplete value can interfere with assistive technologies that rely on these hints.
- User experience: Specific autofill tokens like email, tel, street-address, and current-password allow 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 on to explicitly allow autofill.
- Use off to 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 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 max to be a valid floating-point number when present.
- Predictable validation: An empty max means 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 max attribute 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" or max="99.5").
- Remove the attribute: If no maximum is needed, remove the max attribute entirely rather than leaving it empty.
- Fix dynamic templates: If your HTML is generated from a template, add a conditional check so that max is 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 maxlength attribute to communicate input constraints to users. An empty value could cause confusing or incorrect announcements.
- Maintainability: An empty maxlength is 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 maxlength altogether. 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 min attribute’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 min value, 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 min and max to 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 min values. 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 min to the actual minimum number or date/time string you want to enforce.
- Remove the attribute: If no minimum constraint is needed, simply omit the min attribute. 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 name attribute is present, its value must not be empty.
- JavaScript references: An empty name makes it difficult to reference the element using methods like document.getElementsByName() or FormData.
- Accessibility: Screen readers and assistive technologies may use the name attribute 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 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 type attribute 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/max bounds and matching an optional step value.
- Standards compliance: Invalid type values 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 type to "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, use address-line1, address-line2, and address-line3 instead. 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.
The HTML specification defines specific rules about which autocomplete autofill field names can be paired with which input types. The tel-national token (which represents a phone number without the country code) is classified as requiring a text-based input control. Meanwhile, <input type="tel"> is a specialized control that the spec treats differently from a plain text field. When the validator encounters tel-national on a type="tel" input, it flags the mismatch because the autofill field name is not allowed in that context.
This might seem counterintuitive — a national telephone number value on a telephone input feels like a natural fit. However, the distinction exists because type="tel" already implies a complete telephone number, and the spec maps the broader tel autocomplete token to it. The more granular telephone tokens like tel-national, tel-country-code, tel-area-code, tel-local, tel-local-prefix, and tel-local-suffix are designed for type="text" inputs where a phone number is being broken into individual parts across multiple fields.
Getting this right matters for browser autofill behavior. When the autocomplete value and input type are properly paired according to the spec, browsers can more reliably populate the field with the correct portion of the user’s stored phone number. An invalid pairing may cause autofill to silently fail or behave unpredictably across different browsers.
How to fix it
You have two options:
- Change the input type to text — Use type="text" if you specifically want the national portion of a phone number (without the country code). This is the right choice when you’re splitting a phone number across multiple fields.
- Change the autocomplete value to tel — Use autocomplete="tel" if you want a single field for the full phone number. This pairs correctly with type="tel".
Examples
❌ Invalid: tel-national on type="tel"
<label for="phone">Phone number</label>
<input id="phone" name="phone" type="tel" autocomplete="tel-national">
This triggers the validation error because tel-national is not allowed on a type="tel" input.
✅ Fix option 1: Change input type to text
<label for="phone">Phone number (without country code)</label>
<input id="phone" name="phone" type="text" autocomplete="tel-national">
Using type="text" satisfies the spec’s requirement for the tel-national autofill token. This is ideal when collecting just the national portion of a number.
✅ Fix option 2: Change autocomplete to tel
<label for="phone">Phone number</label>
<input id="phone" name="phone" type="tel" autocomplete="tel">
Using autocomplete="tel" is the correct pairing for type="tel" and tells the browser to autofill the complete phone number.
✅ Splitting a phone number across multiple fields
When you need separate fields for different parts of a phone number, use type="text" with the granular autocomplete tokens:
<fieldset>
<legend>Phone number</legend>
<label for="country-code">Country code</label>
<input id="country-code" name="country-code" type="text" autocomplete="tel-country-code">
<label for="national">National number</label>
<input id="national" name="national" type="text" autocomplete="tel-national">
</fieldset>
In HTML, boolean attributes work differently than you might expect from programming languages. They don’t accept "true" or "false" as values. Instead, the presence of the attribute means it’s active, and its absence means it’s inactive. This is defined in the WHATWG HTML Living Standard, which states that a boolean attribute’s value must either be the empty string or a case-insensitive match for the attribute’s name.
This means there are exactly three valid ways to write the disabled attribute:
- disabled (no value)
- disabled="" (empty string)
- disabled="disabled" (attribute name as value)
Any other value — including "true", "false", "yes", "no", or "1" — is invalid and will trigger a W3C validation error.
A common and dangerous misunderstanding is that disabled="false" will make an element not disabled. It won’t. Because boolean attributes are activated by their presence alone, disabled="false" still disables the element. The browser sees the disabled attribute is present and treats the element as disabled, completely ignoring the "false" value. This can lead to confusing bugs where elements appear permanently disabled.
This issue affects all elements that support the disabled attribute, including <input>, <button>, <select>, <textarea>, <fieldset>, <optgroup>, and <option>. The same rules apply to other boolean attributes like checked, readonly, required, autofocus, and hidden.
Why this matters
- Standards compliance: Using invalid attribute values violates the HTML specification and produces W3C validation errors.
- Maintainability: Developers reading disabled="true" or disabled="false" may misunderstand the intent, especially if they assume "false" removes the disabled state.
- Framework pitfalls: Some JavaScript frameworks dynamically set disabled="true" or disabled="false" as string values. When the rendered HTML reaches the browser, both values result in a disabled element, which is rarely the intended behavior.
How to fix it
- To disable an element, add the disabled attribute with no value, or use disabled="" or disabled="disabled".
- To enable an element, remove the disabled attribute entirely. Don’t set it to "false".
- In JavaScript, use the DOM property element.disabled = true or element.disabled = false, or use element.removeAttribute('disabled') to enable it. Avoid element.setAttribute('disabled', 'false').
Examples
❌ Invalid: using "true" and "false" as values
<form>
<input type="text" disabled="true">
<select disabled="false">
<option>Option A</option>
</select>
<button type="submit" disabled="true">Submit</button>
</form>
Both the "true" and "false" values are invalid. Additionally, disabled="false" still disables the <select> element, which is almost certainly not what was intended.
✅ Valid: correct boolean attribute usage
<form>
<input type="text" disabled>
<select>
<option>Option A</option>
</select>
<button type="submit" disabled="disabled">Submit</button>
</form>
Here, the <input> and <button> are disabled using valid syntax. The <select> is enabled because the disabled attribute has been removed entirely.
✅ Valid: toggling disabled state with JavaScript
<form>
<input type="text" id="username" disabled>
<button type="button" id="toggle">Enable field</button>
<script>
document.getElementById('toggle').addEventListener('click', function () {
var field = document.getElementById('username');
field.disabled = !field.disabled;
});
</script>
</form>
Using the disabled DOM property (a real boolean) is the correct way to toggle the disabled state dynamically. This avoids the pitfall of setting string values like "true" or "false" on the attribute.
When you write an attribute like maxlength="200 and accidentally omit the closing quote, everything that follows — including subsequent attribute names and their values — gets absorbed into that one attribute’s value. In this case, the validator sees the value of maxlength as 200 aria-required= (or similar), which is not a valid integer. The parser doesn’t encounter a closing " until it finds the next quotation mark further along in the tag, causing a cascade of errors.
This is a problem for several reasons:
- Broken functionality: The maxlength attribute won’t work because 200 aria-required= is not a valid number. The browser cannot determine the intended character limit.
- Lost attributes: The aria-required attribute is swallowed into the malformed maxlength value, so it never gets applied as a separate attribute. Assistive technologies like screen readers won’t know the field is required.
- Accessibility impact: Since aria-required="true" is lost, users who rely on screen readers won’t receive the information that the field is mandatory, potentially leading to form submission errors and a frustrating experience.
The root cause is almost always a missing closing quotation mark. Carefully check that every attribute value has both an opening and a closing ". This kind of typo is easy to make and easy to miss, especially in long tags with many attributes.
Examples
Incorrect — missing closing quote on maxlength
The closing " after 200 is missing, so the value of maxlength extends all the way to the next quotation mark it finds:
<input type="text" name="nome" id="nome" maxlength="200 aria-required="true">
The validator interprets maxlength as having the value 200 aria-required=, and only true ends up as the value of an unintended or malformed attribute. Nothing works as expected.
Correct — properly quoted attributes
Each attribute has its own properly matched pair of quotation marks:
<input type="text" name="nome" id="nome" maxlength="200" aria-required="true">
Here, maxlength="200" correctly limits the input to 200 characters, and aria-required="true" is a separate attribute that tells assistive technologies the field is required.
Incorrect — missing closing quote with more attributes
This issue can happen with any combination of attributes. Here, a missing quote after maxlength absorbs class and placeholder:
<input type="text" maxlength="50 class="username" placeholder="Enter name">
Correct — all quotes properly closed
<input type="text" maxlength="50" class="username" placeholder="Enter name">
Tips for avoiding this issue
- Use a code editor with syntax highlighting. Most editors color attribute values differently from attribute names. If you see an attribute name rendered in the same color as a value string, a quote is likely missing.
- Format attributes one per line on complex elements. This makes it much easier to spot mismatched quotes:
<input
type="text"
name="nome"
id="nome"
maxlength="200"
aria-required="true">
- Validate early and often. Running your HTML through the W3C validator regularly helps catch these small typos before they cause confusing bugs in production.
The accept attribute provides browsers with a hint about which file types the user should be able to select through the file picker dialog. While browsers may still allow users to select other file types, the attribute helps filter the file picker to show relevant files first, improving the user experience.
The W3C validator reports this error when it encounters tokens in the accept attribute that don’t conform to the expected format. The attribute value is parsed as a set of comma-separated tokens, and each token must be one of the following:
- A valid MIME type such as application/pdf, text/plain, or image/png (the / character separating type and subtype is required).
- A file extension starting with a period, such as .pdf, .docx, or .jpg.
- One of three wildcard MIME types: audio/*, video/*, or image/*.
Common mistakes that trigger this error include using bare file extensions without the leading dot (e.g., pdf instead of .pdf), using arbitrary words that aren’t valid MIME types, or including spaces in a way that creates malformed tokens. The HTML specification (WHATWG) defines strict rules for how these tokens are parsed, and violating them produces a validation error.
Getting this attribute right matters for several reasons. First, a correctly specified accept attribute helps users by pre-filtering the file picker, so they don’t have to hunt through unrelated files. Second, assistive technologies may use this attribute to communicate accepted file types to users. Third, some browsers may silently ignore a malformed accept value entirely, removing the helpful filtering behavior you intended.
To fix the issue, review each token in your accept attribute and ensure it matches one of the three valid formats listed above. If you’re unsure of the correct MIME type for a file format, consult the IANA Media Types registry. When in doubt, dot-prefixed file extensions (like .pdf or .docx) are often simpler and more readable.
Examples
Incorrect: bare words without dots or MIME type format
<input type="file" name="document" accept="doc, docx, pdf">
The tokens doc, docx, and pdf are neither valid MIME types (no /) nor valid file extensions (no leading .), so the validator rejects them.
Incorrect: spaces creating malformed tokens
<input type="file" name="photo" accept="image/png, image/jpeg">
While most browsers handle spaces after commas gracefully, the validator may flag this depending on parsing. It’s safest to avoid spaces or keep them minimal.
Correct: using dot-prefixed file extensions
<input type="file" name="document" accept=".doc,.docx,.pdf">
Correct: using valid MIME types
<input type="file" name="document" accept="application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/pdf">
Correct: mixing MIME types, extensions, and wildcards
<input type="file" name="media" accept="image/*,.pdf,video/mp4">
This accepts all image types, PDF files, and MP4 videos. Mixing formats is perfectly valid as long as each token individually conforms to the specification.
Correct: using wildcard MIME types for broad categories
<input type="file" name="photo" accept="image/*">
This allows the user to select any image file, regardless of specific format.
In HTML, boolean attributes work differently from regular attributes. They don’t take true or false as values. Instead, the mere presence of the attribute represents the “true” state, and its complete absence represents “false.” According to the WHATWG HTML specification, a boolean attribute has exactly three valid representations: the attribute name alone (checked), an empty string value (checked=""), or the attribute name as its own value (checked="checked"). Any other value — including seemingly intuitive ones like "true", "false", "yes", or "no" — is invalid.
This is particularly important to understand because checked="false" does not uncheck the input. Since the attribute is still present, the browser interprets it as checked. This can lead to confusing behavior where a developer writes checked="false" expecting the checkbox to be unchecked, but it renders as checked. To leave an input unchecked, you must omit the checked attribute entirely.
The checked attribute applies to <input> elements of type checkbox and radio. It sets the initial checked state when the page loads. Note that JavaScript uses a different convention — the checked DOM property accepts true or false as JavaScript boolean values — but this does not apply to HTML markup.
Fixing this issue ensures standards compliance, avoids unexpected behavior, and improves code clarity. While browsers are lenient and will typically treat any value as “checked,” relying on this behavior is non-standard and can cause confusion for developers reading the code.
Examples
Invalid: using a string value
<input type="checkbox" checked="true">
<input type="radio" name="color" value="red" checked="yes">
These produce validation errors because "true" and "yes" are not valid boolean attribute values.
Invalid: attempting to set false
<!-- This does NOT uncheck the input — it's still checked! -->
<input type="checkbox" checked="false">
Despite the value "false", the checkbox will still render as checked because the attribute is present.
Valid: attribute name only (recommended)
<input type="checkbox" checked>
<input type="radio" name="color" value="red" checked>
This is the most common and cleanest form.
Valid: empty string value
<input type="checkbox" checked="">
Valid: attribute name as its own value
<input type="checkbox" checked="checked">
This form is sometimes seen in XHTML-compatible markup.
Valid: omitting the attribute to leave unchecked
<input type="checkbox">
To represent an unchecked state, simply leave the attribute off.
Full example in context
<!DOCTYPE html>
<html lang="en">
<head>
<title>Newsletter Preferences</title>
</head>
<body>
<form>
<fieldset>
<legend>Email preferences</legend>
<label>
<input type="checkbox" name="newsletter" checked>
Subscribe to newsletter
</label>
<label>
<input type="checkbox" name="promotions">
Receive promotional emails
</label>
</fieldset>
<fieldset>
<legend>Frequency</legend>
<label>
<input type="radio" name="frequency" value="daily" checked>
Daily
</label>
<label>
<input type="radio" name="frequency" value="weekly">
Weekly
</label>
</fieldset>
</form>
</body>
</html>
In this example, the newsletter checkbox and the “Daily” radio button are pre-selected using the valid checked attribute without any value. The promotions checkbox and “Weekly” radio button are unchecked because the attribute is absent.
Boolean attributes in HTML work differently from regular attributes. Their mere presence on an element makes them “true,” and their absence makes them “false.” According to the WHATWG HTML specification, a boolean attribute may only have three valid representations:
- The attribute name alone (e.g., disabled)
- The attribute with an empty string value (e.g., disabled="")
- The attribute with its own name as the value (e.g., disabled="disabled")
Any other value — including seemingly intuitive ones like "true", "yes", or "no" — is invalid and will cause the W3C HTML Validator to report an error such as: Bad value “disabled” for attribute “disabled” on element “input” (or a similar message referencing whatever invalid value you used).
Why this matters
Standards compliance: Using invalid values violates the HTML specification, which can lead to unpredictable behavior as browsers evolve.
Misleading behavior: A common pitfall is writing disabled="false" and expecting the input to be enabled. This does not work as expected — because the attribute is still present, the element remains disabled regardless of the value. This can lead to confusing bugs where developers think they’re enabling a field but it stays disabled.
Accessibility: Assistive technologies rely on the DOM’s interpretation of boolean attributes. While browsers typically handle invalid values gracefully by treating any present disabled attribute as true, sticking to valid values ensures the most consistent behavior across screen readers and other tools.
Templating and frameworks: This issue frequently arises when templating engines or JavaScript frameworks insert string values into boolean attributes. If your template outputs disabled="true" or disabled="false", you should instead conditionally include or omit the attribute entirely.
How to fix it
- Remove the value entirely — just write the attribute name by itself.
- Use an empty string — write disabled="" if your tooling requires an explicit value.
- Use the canonical form — write disabled="disabled" if you need XHTML compatibility.
- To enable an element, remove the disabled attribute completely rather than setting it to "false".
Examples
Incorrect usage
These all trigger a validation error because the values are not valid for a boolean attribute:
<input type="text" disabled="yes">
<input type="text" disabled="true">
<input type="text" disabled="false">
<input type="text" disabled="1">
<button disabled="no">Submit</button>
Note that disabled="false" and disabled="no" still disable the element — the browser sees the attribute is present and treats it as true.
Correct usage
All three of these are valid ways to disable an input:
<input type="text" disabled>
<input type="text" disabled="">
<input type="text" disabled="disabled">
To have an enabled input, simply omit the attribute:
<input type="text">
Handling dynamic values in JavaScript
If you need to toggle the disabled state dynamically, use the DOM property rather than setting an attribute value:
<form>
<input type="text" id="username">
<button type="button" id="toggle">Toggle</button>
<script>
document.getElementById("toggle").addEventListener("click", function () {
var input = document.getElementById("username");
input.disabled = !input.disabled;
});
</script>
</form>
Setting element.disabled = true or element.disabled = false in JavaScript correctly adds or removes the attribute from the DOM without producing invalid markup.
Other boolean attributes
This same rule applies to all boolean attributes in HTML, including checked, readonly, required, hidden, autoplay, loop, muted, and others. For example:
<!-- Incorrect -->
<input type="checkbox" checked="true">
<input type="email" required="required_field">
<!-- Correct -->
<input type="checkbox" checked>
<input type="email" required>
When in doubt, use the simplest form: just the attribute name with no value. It’s the most readable, the most concise, and fully compliant with the HTML specification.
The multiple attribute tells the browser that the user can supply more than one value for a given input. For <input type="file">, it allows selecting multiple files at once. For <input type="email">, it allows entering a comma-separated list of email addresses. These are the only two input types that support the multiple attribute according to the HTML specification.
This validation error can appear for two distinct reasons, and sometimes both at once:
-
An invalid value is assigned to the boolean attribute. Boolean attributes in HTML follow strict rules. Valid syntaxes are: the attribute name alone (multiple), an empty string value (multiple=""), or the attribute’s own name as the value (multiple="multiple"). Any other value — including multiple="true", multiple="1", or multiple="yes" — is invalid.
-
The attribute is used on an unsupported input type. Placing multiple on input types like text, number, password, or url is not valid because those types don’t define behavior for this attribute. Browsers will simply ignore it, but it still constitutes invalid markup.
Why this matters
- Standards compliance: Invalid boolean attribute values violate the WHATWG HTML specification. While most browsers are forgiving and may still interpret multiple="true" as the attribute being present, relying on this behavior is fragile and non-standard.
- Accessibility: Assistive technologies rely on valid, well-structured markup. An invalid attribute value could lead to unpredictable behavior in screen readers or other tools.
- Maintainability: Using multiple on an unsupported input type suggests a misunderstanding of the element’s capabilities, which can confuse other developers and lead to bugs.
How to fix it
- Remove the value entirely: Change multiple="1" or multiple="true" to just multiple.
- Use a valid boolean syntax if a value is required: Some templating systems or XML-based contexts (like XHTML) require explicit attribute values. In those cases, use multiple="" or multiple="multiple".
- Ensure the input type supports multiple: Only type="email" and type="file" accept this attribute. If you need multi-value input for other types, consider alternative approaches like multiple separate inputs, a <select multiple> element, or a JavaScript-based solution.
Examples
Invalid: wrong value on a boolean attribute
<!-- Bad: "1" is not a valid boolean attribute value -->
<input type="file" name="attachments" multiple="1">
<!-- Bad: "true" is not a valid boolean attribute value -->
<input type="email" name="recipients" multiple="true">
Invalid: multiple on an unsupported input type
<!-- Bad: type="text" does not support the multiple attribute -->
<input type="text" name="tags" multiple>
<!-- Bad: type="number" does not support the multiple attribute -->
<input type="number" name="quantities" multiple>
Valid: correct usage of multiple
<!-- Correct: boolean attribute with no value -->
<input type="file" name="attachments" multiple>
<!-- Correct: empty string value (valid boolean syntax) -->
<input type="email" name="recipients" multiple="">
<!-- Correct: attribute name as value (valid for XHTML compatibility) -->
<input type="file" name="documents" multiple="multiple">
Full corrected document
<!DOCTYPE html>
<html lang="en">
<head>
<title>Upload Form</title>
</head>
<body>
<form action="/submit" method="post" enctype="multipart/form-data">
<label for="recipients">Recipients:</label>
<input type="email" id="recipients" name="recipients" multiple placeholder="a@example.com, b@example.com">
<label for="files">Attachments:</label>
<input type="file" id="files" name="files" multiple>
<button type="submit">Send</button>
</form>
</body>
</html>
Ready to validate your sites?
Start your free trial today.