Lesson 10 · Part 2 · Forms

Constructing forms

A form is a conversation. Every box needs a name, every problem needs words, and every group needs a title. Get those three right and most people, including screen reader users, can finish the form without help.

In one minute

In plain words

Picture a paper form handed to someone who cannot see it, and read aloud one box at a time. If the clerk says only "box, box, box", nobody can fill it in. A label is the clerk saying "Work email". An error is the clerk saying "That one is wrong, because it needs an @ sign". A legend is "Now the delivery questions".

1. The label is the name, not the placeholder

Browsers build a list of every control on the page, with a name for each. Assistive technology reads that name out. This list is called the accessibility tree. In plain words, it is the page as a screen reader sees it: roles, names and states instead of pixels. A <label for> supplies the name. It also makes the label clickable, which enlarges the tap target (WCAG 2.5.8 asks for at least 24 by 24 CSS pixels).

Lab: placeholder or label?

Step 1. Pick a version, then type a few letters into the box.


          

With the placeholder version, the question vanishes the moment you type. People with memory or attention difficulties lose track of what the box wants. Placeholder text is also usually low contrast (WCAG 1.4.3 needs 4.5:1) and some screen readers skip it. Zoom makes it worse: at 400% a long placeholder is cut off.

2. Errors need words, and a link to the field

A red border tells only people who can see red. WCAG 1.4.1 says colour must not be the only signal, and 3.3.1 says an error must be described in text. Three attributes do the work: aria-invalid="true" marks the field, aria-describedby points at the message, and role="alert" announces it as it appears.

Lab: submit the form with a bad email

Step 2. Choose a version, type nothing (or "ada"), then press Create account.

We use this for the receipt only.


          
Submit the form to see the result.
Submit the form to hear it.

3. Groups need a legend

Two radio buttons named "Pickup" and "Ship" make no sense alone: pickup of what? A <fieldset> wraps the group and its <legend> is read before each option, so the question travels with the answers.

Lab: tab into the radio group

Step 3. Pick a version, then press Tab into the options and use the arrow keys.


          
Move focus onto an option.

Arrow keys move between radios in one group and Tab leaves the group. That works only when the radios share a name. Use a fieldset for checkbox groups too. Do not nest a fieldset for a single control.

4. Multi-step forms: tell the user where they are

When you swap in the next step, the button the user just pressed often disappears. The browser then drops focus back to the top of the page, so a keyboard user starts again and a screen reader user hears nothing. The fix is small: put the step in the heading (for example "Step 2 of 3: Delivery"), and move focus to that heading. Give the heading tabindex="-1" so script can focus it without adding a tab stop.

Lab: press Next step

Step 4. Pick a version. Press Tab to the button, then Enter. Watch where focus lands.

Press Next step.
Press Next step.

Also put the step in the URL (for example ?step=2) so Back works and a refresh does not lose the place. Keep what the user typed when they go back. WCAG 3.3.7 (Redundant Entry) says do not ask for the same information twice.

Common mistakes

You seeWhy it is a problemFix
Placeholder used as the labelDisappears while typing; low contrast; unreliable name.<label for> that stays visible.
Red border, no messageColour-blind and screen reader users get nothing.Text message + aria-invalid + aria-describedby.
Error shown only in a toast that fadesGone before it can be read or found.Keep it in the page next to the field.
Radios with no legendOptions are heard without the question.fieldset + legend.
Next step drops focusKeyboard user restarts from the top.Focus the new step's heading.
Blocking paste in password or email boxesBreaks password managers (WCAG 3.3.8).Allow paste and autofill.
Try it yourself
  1. In lab 2, submit the form empty with "Red border only". Then switch to "Text + linked" and submit again. Compare the readout.
  2. Press Tab through lab 3 with no fieldset, and read what you would hear for "Ship".
  3. Zoom your browser to 400%. Is every label still visible next to its box?

Remember

Edit this example in the playground