The type is not decoration
type changes four things at once: the control you get, the keyboard a phone shows, the validation the browser applies, and what assistive technology announces. Choosing text for everything throws all four away.
Every type worth knowing
| type | What you get | Watch out for |
|---|---|---|
text | The default single line. | Use it only when nothing more specific fits. |
email | Email keyboard, format check, multiple for a list. | The check is loose by design — it accepts a@b. |
tel | A phone keypad. | No validation at all. Add pattern if you need a shape. |
url | URL keyboard and validation. | Requires a scheme: example.com is invalid, https://example.com is not. |
number | A numeric field with min, max, step and spinners. | Rejects spaces, commas and leading zeros. Bad for phone numbers, card numbers and postcodes. |
range | A slider. | No visible value — show it yourself in an <output>. |
date time datetime-local month week | The native picker. | The value is always ISO (2026-09-21) whatever the display format. |
password | Masked input. | Pair with autocomplete="current-password" or "new-password". |
search | A field with a clear button. | Nothing else differs from text. |
checkbox | On or off. | Unchecked boxes are not submitted at all. |
radio | One of a set. Same name makes a group. | Always wrap a group in a fieldset with a legend. |
file | A file picker. accept, multiple, capture. | Cannot be styled — style a label and hide the input. |
color | A colour picker returning #rrggbb. | No alpha channel. |
hidden | Sent, never shown. | Not a security measure: anyone can edit it. |
number is narrower than it looks
type="number" is for quantities you would do arithmetic on. It is the wrong choice for anything that is a string of digits: a phone number, a card number, a postcode, a PIN. Those lose leading zeros, reject spaces, and get a spinner that makes no sense.
<!-- wrong -->
<input type="number" name="pin">
<!-- right: a text field with a numeric keyboard and a shape -->
<input type="text" name="pin" inputmode="numeric"
pattern="[0-9]{4}" title="Four digits" autocomplete="one-time-code">
inputmode chooses the keyboard
inputmode changes the on-screen keyboard without changing the type or the validation. It is the tool for "digits, but not a number".
numeric— digits only. PINs, codes, quantities typed as text.decimal— digits and a decimal separator.tel— the phone keypad, with + and *.email— adds @ and a dot.url— adds / and .com.search— the Enter key becomes Search.
autocomplete is one attribute for a large win
autocomplete names what a field holds so the browser and the password manager can fill it in one tap. It measurably raises completion rates and it is required by accessibility guidelines for fields about the person filling the form.
<input autocomplete="name">
<input autocomplete="given-name"> <input autocomplete="family-name">
<input autocomplete="email"> <input autocomplete="tel">
<input autocomplete="organization">
<input autocomplete="street-address">
<input autocomplete="address-level2"> <!-- city -->
<input autocomplete="postal-code"> <input autocomplete="country-name">
<input autocomplete="cc-number"> <input autocomplete="cc-exp">
<input autocomplete="username">
<input autocomplete="current-password"> <input autocomplete="new-password">
<input autocomplete="one-time-code">
autocomplete="off" on a password or address field is a habit from old banking sites. Browsers largely ignore it now, and where they do not, you have made the form harder for everyone — including people who cannot type easily.
Checkboxes and radios
- An unchecked checkbox sends nothing. A server reading a missing key as "false" is the normal pattern.
- Without a
value, a checked box sendson. Give it a real value. - Radios sharing a
nameare one control: checking one unchecks the rest, and arrow keys move between them. - Several checkboxes may share a name to send a list:
interests=html&interests=css.
<label><input type="checkbox" name="terms" value="yes"> I agree</label>
<!-- checked → terms=yes
unchecked → nothing at all is sent -->
<fieldset>
<legend>Delivery</legend>
<label><input type="radio" name="delivery" value="standard" checked> Standard</label>
<label><input type="radio" name="delivery" value="express"> Express</label>
</fieldset>
Files
accept filters the picker but does not enforce anything — check the type and size on the server. A form containing a file input must use method="post" and enctype="multipart/form-data".
<label for="plan">Floor plan</label>
<input type="file" id="plan" name="plan"
accept=".pdf,image/*" multiple>
readonly, disabled and required
The row that catches people: a disabled field is not sent. If you want the value to arrive but not be edited, use readonly.
| Attribute | Focusable | Submitted | Validated |
|---|---|---|---|
readonly | Yes | Yes | No |
disabled | No | No | No |
required | Yes | Yes | Yes |