Asking the Visitor for Something
A form is the only part of HTML that sends data anywhere, and one unlabelled input is the difference between a usable form and an unusable one. Meet the input types that give phones the right keyboard for free.
Worth reading first: Elements, Tags, and the Ones That Never Close
A form is the only part of HTML that sends data anywhere, and one unlabelled input is the difference between a usable form and an unusable one. Meet the input types that give phones the right keyboard for free.
A form wraps the inputs and says where they go
<form> is a container with two attributes that decide what happens when it is submitted.
<form action="/subscribe" method="post"> <label for="email">Email address</label> <input type="email" id="email" name="email" required> <label for="message">Message</label> <textarea id="message" name="message" rows="4"></textarea> <button type="submit">Send</button></form>Where the data goes. A URL on your server, or a form service. Omit it and the form submits back to the current page.
post puts the data in the request body — for anything that changes something, or is private. get puts it in the URL as ?name=value, which is right for a search box because the result is then linkable.
On each input, this is the KEY the server receives. An input with no name is not submitted at all — this is the commonest reason a field silently goes missing.
Different job entirely: it joins the input to its label. You need both, and they usually match.
Every input needs a label that is joined to it
This is the most important paragraph in the chapter. A <label> must be programmatically associated with its input, not merely sitting next to it.
<!-- for matches id --><label for="email">Email address</label><input type="email" id="email" name="email"> <!-- or wrap it, and no ids are needed --><label> Email address <input type="email" name="email"></label>Two things follow, and one of them is visible to everybody:
For screen reader users
Focusing the field announces "Email address, edit text". Unlabelled, it announces "edit text" — a box with no indication of what belongs in it, in a form of six identical boxes.
For everybody
Clicking the label focuses the field, which makes the whole label a target rather than the box alone. On a checkbox that turns a 16-pixel tap target into a comfortable one, which matters enormously on a phone.
Group related controls with fieldset and legend — a set of radio buttons genuinely needs this, because each radio has its own label and the question they answer has nowhere else to live.
<fieldset> <legend>How did you hear about us?</legend> <label><input type="radio" name="source" value="friend"> A friend</label> <label><input type="radio" name="source" value="search"> Search</label> <label><input type="radio" name="source" value="social"> Social media</label></fieldset>Note the shared name. That is what makes them one group where selecting one deselects the others — radios with different names are unrelated and all selectable at once.
The type attribute changes the keyboard
<input> is one element that becomes about twenty different controls depending on its type. On a phone, the type also decides which keyboard appears — which is real usability for one attribute.
The default. Anything.
Keyboard with @ and a dot. Validates the shape on submit.
Numeric keypad. No validation — phone number formats vary too much worldwide to check.
Keyboard with / and .com. Requires a scheme like https://.
Numeric, with min, max, and step. Not for phone numbers, postcodes, or card numbers — those are text that happens to contain digits.
Masks the characters. Note that it does not encrypt anything; that is https's job.
A native picker, which differs between browsers and is usually still better than three dropdowns.
Many-of-many, and one-of-many. Radios in a group share a name.
A file picker. Add accept="image/*" to filter it.
A search box with a clear button, a colour picker, and a slider respectively.
The other form controls are separate elements rather than input types:
<textarea id="bio" name="bio" rows="5"></textarea> <select id="country" name="country"> <option value="">Choose…</option> <option value="uk">United Kingdom</option> <option value="in">India</option></select> <button type="submit">Send</button><button type="button">Does not submit</button>Also note the closing tag on <textarea> — it wraps content, unlike <input>, which is void. Any whitespace between the tags becomes the field's starting value, so write them together.
placeholder is not a label, and never was
Placeholder text looks like a tidy way to skip labels. It fails in four separate ways, and it is worth knowing all of them because the design keeps coming back.
<input type="email" name="email" placeholder="Email address">The moment somebody types. Now they cannot check what the field was for, and neither can anyone reviewing a half-filled form.
Grey on white by design, so it is the least readable text on the page — and darkening it makes the field look already filled in.
Some announce it, some do not, some read it only when the field is empty. It is not a reliable label anywhere.
Users skip fields that appear to already have content. This is a documented, repeated finding in usability testing.
Use a real label. Placeholder is for an example that supplements the label — "e.g. +44 7700 900123"under a "Phone number" label — and even then a small hint below the field is usually better, because it stays visible.
<label for="phone">Phone number</label><input type="tel" id="phone" name="phone" aria-describedby="phone-hint"><p id="phone-hint" class="hint">Include the country code, e.g. +44 7700 900123</p>The browser validates before your server does
Several attributes make the browser check a field before it will submit, with no JavaScript at all.
<input type="email" required><input type="text" minlength="2" maxlength="60" required><input type="number" min="1" max="120" step="1"><input type="text" pattern="[A-Za-z0-9]{6}" title="Six letters or digits">Cannot be empty. Boolean — present means true.
Character limits on text. maxlength stops typing; minlength blocks submission.
For numbers, dates, and ranges.
A regular expression. Always add a title — it becomes the error message, and without one the browser says something unhelpfully generic.
One more attribute worth knowing, because it is invisible and helps enormously: autocomplete. It tells the browser what a field is for, so it can offer the right saved value.
<input type="email" name="email" autocomplete="email"><input type="text" name="name" autocomplete="name"><input type="text" name="addr" autocomplete="street-address"><input type="password" name="pw" autocomplete="current-password">There are about fifty defined values. Using them means fewer keystrokes for everybody and far fewer for anyone with a motor impairment — which is the pattern for most of this chapter: the accessible version is also the more convenient one.
Key takeaways
- A form's action says where data goes; method post puts it in the body, get puts it in the URL.
- An input with no name attribute is not submitted at all.
- name is the key the server sees; id is what joins the input to its label. You need both.
- Every input needs a label joined by for/id or by wrapping. Clicking the label focuses the field.
- fieldset and legend group radios and checkboxes, because the question needs somewhere to live.
- Radios in one group share a name; different names means they are unrelated.
- type changes the phone keyboard as well as the control. Use tel, email, url, number appropriately.
- A <button> in a form submits unless you write type="button".
- placeholder is not a label: it vanishes, is low contrast, is inconsistently announced, and makes fields look filled.
- required, pattern, min and max are conveniences that can be removed in devtools — always validate on the server too.
- autocomplete lets the browser fill fields in, which helps everybody and helps some people a great deal.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.