# Form inputs have labels

- **Pattern:** `ab-002585` (`project-snapshot.legal.form-inputs-have-labels`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-25
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002585
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002585)

## Why it matters

An `<input>` without `<label htmlFor>`, `aria-label`, or a wrapping `<label>` parent is announced by screen readers as "edit text" with no indication of what the field is for — on a checkout or signup form this turns every submission into a guessing game and drives abandonment. The Target v. NFB $6M settlement cited form-label failures as a lead claim, and unlabeled form fields sit as the #2 most common claim in the ~3,600 annual federal ADA website lawsuits tracked by Seyfarth Shaw (#1 is missing alt text). The most common anti-pattern is using the `placeholder` attribute as the sole label, which visually looks labeled to a sighted developer but disappears on focus, provides no accessible name, and forces users with cognitive or memory impairments to delete their input just to re-read the prompt. AI coding tools produce this failure constantly because modern Tailwind-shaped UI snippets often omit `<label>` in favor of placeholder text for visual compactness, and because form libraries that wire up labels via `id` wiring frequently lose the wiring when a model refactors a form. Browser autofill and password managers also rely on labels to decide what to autofill, so unlabeled fields quietly break password saving.

## Severity rationale

High because form-label failures drove the Target v. NFB $6M settlement and rank as the #2 claim in ~3,600 annual federal ADA website lawsuits — the exposure is an active plaintiff's-bar target, not theoretical.

## Remediation

Pair each input with a `<label>`:
  ```tsx
  <label htmlFor="email">Email</label>
  <input id="email" name="email" type="email" />
  ```

Deeper remediation guidance and cross-reference coverage for this check lives in the `accessibility-fundamentals` Pro audit — run that after applying this fix for a more exhaustive pass on the same topic.

## Detection

- **ID:** `form-inputs-have-labels`
- **Severity:** `high`
- **What to look for:** Enumerate `<input>`, `<textarea>`, `<select>`, and custom form controls (`<Combobox>`, `<Switch>`, `<Checkbox>`, `<RadioGroup>`, shadcn controls) in JSX/TSX. Exclude `type="hidden|submit|button|reset|image"` from the denominator. For each remaining input, check for one of: (a) `<label htmlFor={id}>` with matching `id`; (b) a wrapping `<label>` containing both input and text; (c) `aria-label="..."`; (d) `aria-labelledby="..."` pointing at visible text; (e) shadcn `<FormField>` + `<FormLabel>`; (f) shadcn `<Label htmlFor={id}>` paired with the input.
- **Pass criteria:** ≥98% of detected inputs (after exclusions) have an association via one of the six mechanisms.
- **Fail criteria:** >2% unlabeled. Placeholder-as-label auto-fails — `<input placeholder="Email" />` disappears on focus and doesn't announce to screen readers. A `<span>`/`<div>` of label-looking text adjacent without `htmlFor`/`aria-labelledby` wiring does NOT count. shadcn `<Label>` without `htmlFor` matching the input's `id` does NOT count.
- **Skip (N/A) when:** No form inputs. Quote: `"0 <input>/<textarea>/<select> elements (after exclusions) in JSX/TSX"`.
- **Report even on pass:** `"N inputs (after exclusions); M have labels via {mechanism}. Coverage M/N = X%"`.
- **Detail on fail:** `"3 of 32 inputs unlabeled (9% — should be ≤2%). Placeholder-only at components/search-bar.tsx:12; app/contact/page.tsx:24 (<input name='phone' />)"`.
- **Remediation:**
  ```tsx
  <label htmlFor="email">Email</label>
  <input id="email" name="email" type="email" />
  ```

Taxons: project-snapshot, accessibility

HTML version: https://auditbuffet.com/patterns/ab-002585
