Browse by section

Web Design 日本語

Getting the Most Out of the HTML input Element

The three attributes with the biggest effect on <input> usability are autocomplete, inputmode, and enterkeyhint. On mobile in particular, getting these right measurably shortens how long a form takes to fill in.

To be precise about the framing: inputmode and autocomplete are not 2023 additions. They had been in the specification for years; what happened around 2023 is that they became safe to rely on in every browser. This article covers how to use them and the places people trip.

Sponsored

type, inputmode, and pattern do different jobs

They look similar and are not interchangeable. Confusing them produces bugs like “letters get into a numbers-only field” and “the mobile keyboard never switches.”

Attribute Controls Does not control
type Value interpretation, built-in validation, UI (calendar etc.) —
inputmode Which soft keyboard appears on mobile What can be entered (appearance only)
pattern Format checking at submit time Keyboard switching; blocking keystrokes

The recurring real-world problem is postal codes, phone numbers, and card numbers. Do not use type="number" for these:

  • Leading zeros are dropped (0120 becomes 120)
  • Hyphens cannot be entered
  • A spinner appears, and the mouse wheel silently changes the value
  • Long numbers can render in exponential notation

The right combination is type="text" plus inputmode="numeric" plus pattern.

<!-- do not do this -->
<input type="number" name="zip">

<!-- do this -->
<label for="zip">Postal code</label>
<input type="text" id="zip" name="zip"
       inputmode="numeric"
       autocomplete="postal-code"
       pattern="\d{3}-?\d{4}"
       title="Enter as 123-4567 or 1234567"
       maxlength="8">

type="number" is right only for quantities and ages—values you actually want to increment. Even then, always supply min, max, and step.

inputmode values

Value Keyboard Use for
numeric Digits only Postal codes, verification codes, card numbers
decimal Digits plus decimal point Amounts, weights
tel Phone keypad (with * and #) Phone numbers
email Includes @ Email addresses
url Includes / and .com URLs
search Enter becomes “Search” Search fields
none No keyboard When you render your own keypad

autocomplete is the biggest single win

Nothing else moves the needle as much. Correct tokens let browsers and password managers fill saved addresses, names, and card details in one tap.

The catch: invented values like autocomplete="on" or autocomplete="fullname" do nothing. You must use the tokens defined in the specification.

<input type="text"  name="lname" autocomplete="family-name">
<input type="text"  name="fname" autocomplete="given-name">
<input type="email" name="email" autocomplete="email">
<input type="tel"   name="tel"   autocomplete="tel">
<input type="text"  name="zip"   autocomplete="postal-code">
<input type="text"  name="addr1" autocomplete="address-level1"> <!-- state/prefecture -->
<input type="text"  name="addr2" autocomplete="address-level2"> <!-- city -->
<input type="text"  name="addr3" autocomplete="address-line1">  <!-- street -->

Login and sign-up

Getting this wrong makes password managers misbehave. The values differ between logging in and signing up.

<!-- login -->
<input type="text"     name="user" autocomplete="username">
<input type="password" name="pass" autocomplete="current-password">

<!-- sign-up and password change -->
<input type="password" name="new"  autocomplete="new-password">

<!-- SMS verification code (auto-filled) -->
<input type="text" name="code"
       inputmode="numeric" autocomplete="one-time-code" maxlength="6">

autocomplete="one-time-code" is what lets iOS and Android fill an SMS verification code automatically. Its presence or absence visibly changes two-factor completion rates.

Sponsored

enterkeyhint changes the action key

The bottom-right key on a mobile keyboard defaults to “return” or “done”. enterkeyhint turns it into “search”, “send”, or “next”.

<input type="search" enterkeyhint="search" placeholder="Search this site">

<input type="text"  enterkeyhint="next"   name="lname">
<input type="text"  enterkeyhint="next"   name="fname">
<input type="email" enterkeyhint="send"   name="email">

The values are enter, done, go, next, previous, search, and send. Simply putting next on a multi-field form removes a lot of thumb travel.

Know what datalist cannot do

<datalist> is convenient, but understand that values outside the list can still be entered. If the choices must be enforced, use <select>.

<label for="pref">Prefecture</label>
<input type="text" id="pref" name="pref" list="prefectures"
       autocomplete="address-level1">
<datalist id="prefectures">
  <option value="Hokkaido"></option>
  <option value="Tokyo"></option>
  <option value="Osaka"></option>
  <option value="Fukuoka"></option>
</datalist>

Practical limitations:

  • Values outside the list submit fine—enforce with pattern or on the server
  • The dropdown cannot be styled with CSS; it is browser UI
  • Filtering behaviour is browser-dependent—prefix versus substring matching is not standardised
  • It gets unwieldy past a few dozen entries; move to <select> or a custom combobox

Sponsored

Writing your own error messages

The default message (“Please fill out this field”) is often unhelpful. setCustomValidity() replaces it.

const zip = document.getElementById('zip');

zip.addEventListener('input', () => {
  // must clear first, or a corrected field stays invalid
  zip.setCustomValidity('');

  if (zip.validity.valueMissing) {
    zip.setCustomValidity('Please enter a postal code');
  } else if (zip.validity.patternMismatch) {
    zip.setCustomValidity('With or without a hyphen is fine (e.g. 123-4567)');
  }
});

Calling setCustomValidity('') at the top is mandatory. Until you pass an empty string, the field stays invalid, so a user who fixes their input still cannot submit. This is a very common bug.

The validity object tells you exactly why a value failed:

Property Meaning
valueMissing required but empty
typeMismatch Not a valid email or url
patternMismatch Does not match pattern
rangeUnderflow / rangeOverflow Outside min / max
tooShort / tooLong Violates minlength / maxlength
stepMismatch Not on the step increment

File inputs and reading values

accept narrows the file picker, and on mobile capture opens the camera directly.

<!-- images only -->
<input type="file" name="photo" accept="image/*" multiple>

<!-- open the rear camera on mobile -->
<input type="file" name="doc" accept="image/*" capture="environment">

accept is a filter on the picker dialog, not validation. Users can switch it to “All files”. Always check type and size on the server.

For dates and numbers, read the typed properties rather than the string:

const num = document.getElementById('num');
const day = document.getElementById('day');

num.valueAsNumber;  // number (NaN when empty)
day.valueAsDate;    // Date object (UTC-based — mind the offset)

Can you use the switch attribute yet?

Not in production. <input type="checkbox" switch> shipped in Safari 17.4, but as of September 2026 Chrome and Firefox do not support it.

That said, unsupported browsers render it as a working checkbox, so it is safe as progressive enhancement.

<label>
  <input type="checkbox" switch name="notify" checked>
  Receive notifications
</label>

If you need an identical switch everywhere today, you have to build it in CSS. Even then, a checkbox recoloured with accent-color is more robust for keyboard and screen reader users than one rebuilt with appearance: none.

Summary

  • Never use type="number" for postal codes, phone numbers, or card numbers. Use type="text" + inputmode="numeric" + pattern
  • Use the specified autocomplete tokens. Do not mix up current-password and new-password
  • Use autocomplete="one-time-code" for SMS verification
  • enterkeyhint turns the action key into “next” or “search”
  • <datalist> accepts values outside the list. Use <select> to enforce
  • Always clear setCustomValidity('') before setting a new message
  • accept is not validation. Verify on the server
  • The switch attribute is Safari-only; fine as progressive enhancement

Form-level design and :user-invalid styling are covered in HTML form features worth using today, and confirmation dialogs in the HTML dialog element.