HTML Guides for text
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 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 required attribute is a boolean attribute that tells the browser a field must be filled in before the form can be submitted. However, not every input type supports this concept. Some input types always have a value (like range, which defaults to a midpoint, or color, which defaults to #000000), while others represent actions rather than user data (like submit, reset, image, and button). For hidden inputs, the user has no way to interact with the field at all, so requiring them to provide a value makes no sense.
The HTML specification explicitly limits required to the following input types: checkbox, date, datetime-local, email, file, month, number, password, radio, search, tel, text, time, url, and week.
Using required on an unsupported type is invalid HTML. Browsers will typically ignore the attribute in this situation, which means you might believe a field is required when it actually isn’t being validated at all. This can lead to forms being submitted with missing or unexpected data. It also creates confusion for assistive technologies — screen readers may announce a field as required even though the browser won’t enforce it, misleading users.
How to fix it
- Check the input type. If you’re using required on an input with a type like hidden, range, color, submit, reset, image, or button, the attribute is not allowed.
- Remove the required attribute if the input type inherently provides a value or doesn’t accept user-provided data.
- Change the input type if you actually need the field to be required and the current type doesn’t match your intent.
- Use server-side validation for inputs like hidden that can’t use required but still need a value.
Examples
❌ Invalid: required on a hidden input
<form>
<input type="hidden" name="token" required>
<button type="submit">Submit</button>
</form>
The user cannot interact with a hidden input, so required is not allowed here. The browser won’t enforce it.
❌ Invalid: required on a range input
<form>
<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100" required>
<button type="submit">Submit</button>
</form>
A range input always has a value (it defaults to the midpoint), so required is meaningless and not permitted.
❌ Invalid: required on a color input
<form>
<label for="color">Pick a color:</label>
<input type="color" id="color" name="color" required>
<button type="submit">Submit</button>
</form>
A color input always has a value (defaulting to #000000), so required is not valid here.
✅ Valid: required removed from unsupported types
<form>
<input type="hidden" name="token" value="abc123">
<label for="volume">Volume:</label>
<input type="range" id="volume" name="volume" min="0" max="100">
<button type="submit">Submit</button>
</form>
✅ Valid: required on supported input types
<form>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="dob">Date of birth:</label>
<input type="date" id="dob" name="dob" required>
<label>
<input type="checkbox" name="terms" required>
I agree to the terms
</label>
<button type="submit">Submit</button>
</form>
These input types — email, date, and checkbox — all accept direct user input and are on the allowed list for the required attribute.
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 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>
Ready to validate your sites?
Start your free trial today.