Skip to content

Forms People Can Use

A form that sighted users can fill in may be completely unusable for a screen reader user if the labels are missing or broken. Accessible forms are not a special case — they are correctly written HTML forms.

When a screen reader user encounters a form, they navigate field by field. As focus moves to each input, the screen reader announces the input’s accessible name — typically the label — followed by the input type.

For example, moving to a correctly labeled email field is announced as something like: “Email address, edit text.”

Without a label, the screen reader announces only the input type: “Edit text.” The user does not know what they are supposed to type. If there are three unlabeled inputs in a row, all three sound identical.

Keyboard users face the same issue. They can reach every input by pressing Tab, but they cannot fill it in correctly without knowing what it is for.

Connecting labels to inputs with for and id

Section titled “Connecting labels to inputs with for and id”

The standard mechanism for associating a label with an input is the for attribute on <label> and the matching id on the input.

<!-- Disconnected: looks right visually, but the label and input are not associated -->
<label>Email address</label>
<input type="email" name="email">
<!-- Connected: for on label matches id on input -->
<label for="email">Email address</label>
<input type="email" id="email" name="email">

When the for and id values match, the browser creates an association. The screen reader announces the label text when the input receives focus. Clicking the label also moves focus to the input — a usability benefit for all users.

Every input needs a label. This includes text fields, email fields, password fields, checkboxes, radio buttons, <select> menus, and <textarea> elements.

An alternative to for/id is wrapping the input inside the label. The browser creates the same association automatically.

<label>
Email address
<input type="email" name="email">
</label>

Both patterns work. The for/id pattern is more common because it allows more flexible layout — the label and input do not need to be adjacent in the HTML.

Section titled “Grouping related fields with fieldset and legend”

When a form contains a group of related inputs — particularly radio buttons or checkboxes — the individual labels are not enough. The user needs to know the question those options answer.

<!-- Missing group context: user hears "Day hike, radio button" but not what the question is -->
<label><input type="radio" name="tour-type" value="day"> Day hike</label>
<label><input type="radio" name="tour-type" value="multi"> Multi-day</label>
<!-- Correct: fieldset and legend provide the group question -->
<fieldset>
<legend>Tour type</legend>
<label><input type="radio" name="tour-type" value="day"> Day hike</label>
<label><input type="radio" name="tour-type" value="multi"> Multi-day</label>
</fieldset>

With <fieldset> and <legend>, the screen reader announces the legend text when the user enters the group — something like: “Tour type, group. Day hike, radio button, 1 of 2.”

Use <fieldset> whenever you have a group of inputs that belong together under a shared question or heading. This is required for radio buttons and checkboxes, and appropriate for logically grouped fields like billing address vs shipping address.

Inaccessible form:

<form>
<p>Name</p>
<input type="text" name="name">
<p>Email</p>
<input type="email" name="email">
<p>Preferred tour</p>
<input type="radio" name="tour" value="pine"> Pine Ridge Loop
<input type="radio" name="tour" value="river"> River Canyon Trail
<input type="submit" value="Send">
</form>

Problems: <p> elements are not labels. No for/id associations. No fieldset for the radio group. The submit button has a value but it is also not associated with anything — it is fine, but the form itself is broken.

Accessible form:

<form>
<div>
<label for="name">Name</label>
<input type="text" id="name" name="name">
</div>
<div>
<label for="email">Email</label>
<input type="email" id="email" name="email">
</div>
<fieldset>
<legend>Preferred tour</legend>
<label><input type="radio" name="tour" value="pine"> Pine Ridge Loop</label>
<label><input type="radio" name="tour" value="river"> River Canyon Trail</label>
</fieldset>
<button type="submit">Send</button>
</form>

Every input has a label. The radio group has a <fieldset> and <legend>. The submit is a <button> with descriptive text.

A common mistake is using placeholder as a substitute for a label.

<!-- Wrong: placeholder disappears when typing begins -->
<input type="text" placeholder="Your name" name="name">

Placeholder text disappears the moment the user starts typing. A user who tabs away and tabs back cannot see what the field is for. Screen readers announce placeholder text inconsistently. Some screen readers do not announce it at all.

Use labels for accessible names. Use placeholder for supplementary hints if helpful — not as a replacement for a label.

Open contact.html from the Summit Trail Outfitters project and review the contact form.

Work through this checklist:

  1. Every <input>, <textarea>, and <select> has an associated <label> — connected via for/id, or by wrapping the input inside the label.

  2. No <p> or <div> is standing in for a label — if you see a paragraph before an input with no <label>, fix it.

  3. Radio buttons and checkboxes are grouped with <fieldset> and <legend> — if the contact form has a “Preferred contact method” or “Tour interest” group, wrap it.

  4. No input uses placeholder as its only label — add a visible <label> for every field.

  5. The submit control is a <button type="submit"> with a clear label.

Also review the newsletter form on index.html — it typically has a single email input. Confirm it has a label even if visually it appears label-free.

  • Screen readers announce the label when an input receives focus — without a label, the user hears only the input type.
  • Connect labels to inputs using for on <label> and matching id on the input.
  • Alternatively, wrap the input inside the <label> element.
  • Group related radio buttons and checkboxes inside <fieldset> with a <legend> that names the group.
  • Placeholder text is not a label — it disappears when typing begins and is announced inconsistently.
  • Every form control needs a label. No exceptions.