Forms and inputs

Build a form that is labelled, validated by the browser and readable by a server.

11of 22
≈ 13Min read
7Parts

What a form is for

A <form> collects named values and sends them somewhere. action is the URL, method is get (values in the query string — searches, filters) or post (values in the body — anything that changes data).

Only controls with a name are sent. A field with no name is invisible to the server, which is the most common reason a form "does not work".

the shape of it
<form action="/subscribe" method="post">
  <label for="email">Email</label>
  <input type="email" id="email" name="email" required>
  <button type="submit">Subscribe</button>
</form>

Every control needs a label

A placeholder is not a label: it disappears as soon as someone types, it fails contrast tests, and many screen readers ignore it. Use a real <label>.

  • <label for="id"> — the for matches the input's id. Works anywhere on the page.
  • <label>Email <input …></label> — wrapping needs no ids at all.
Why it matters beyond accessibility

A connected label makes the label itself clickable, which enlarges the hit area of small checkboxes and radios enormously on a phone.

Input types earn their keep

typeWhat you get
textThe default.
emailEmail keyboard on phones, format validation.
telA numeric keypad. No validation — use pattern.
urlURL keyboard and validation.
numberNumeric input with min, max, step.
date / timeThe native picker.
passwordMasked characters.
searchA clear button in most browsers.
checkbox / radioRadios with the same name form one group.
fileaccept limits the types, multiple allows several.
hiddenSent but not shown.

Validation without JavaScript

The browser can enforce a surprising amount before anything is sent, and it announces the failure for you.

  • required — cannot be empty.
  • minlength / maxlength — length of text.
  • min / max / step — range of numbers and dates.
  • pattern="[0-9]{4}" — a regular expression. Always pair it with a title explaining the format.
a validated field
<label for="pin">4-digit PIN</label>
<input type="text" id="pin" name="pin"
       inputmode="numeric" pattern="[0-9]{4}"
       title="Four digits" required>
Client-side validation is a convenience, not a defence

Anyone can send a request without your page. Every rule you enforce in the browser has to be enforced again on the server.

Autocomplete

autocomplete tells the browser and the password manager what a field is, so it can be filled in one tap: name, email, tel, street-address, postal-code, cc-number, current-password, new-password, one-time-code. It is one attribute and it measurably improves completion rates.

Select, textarea, button

the other three controls
<label for="finish">Finish</label>
<select id="finish" name="finish">
  <option value="">Choose…</option>
  <optgroup label="Painted">
    <option value="matt">Matt</option>
    <option value="satin" selected>Satin</option>
  </optgroup>
  <option value="veneer">Veneer</option>
</select>

<label for="note">Notes</label>
<textarea id="note" name="note" rows="4"></textarea>

<button type="submit">Send</button>
<button type="button">Just a button</button>
A button inside a form submits by default

If a button is not meant to submit, you must write type="button". This single omission causes more surprise page reloads than anything else in HTML.

Grouping

<fieldset> groups related controls and <legend> names the group. For a set of radio buttons this is what tells a screen reader user what the choice is about — without it they hear the options with no question.

a radio group
<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>

Practice