accessibility
Label every form input
MetricSpot checks every form input for an associated <label>, aria-label, or aria-labelledby. Unlabeled fields are announced as just "edit text" by screen readers.
What this check does
Walks every <input>, <textarea>, and <select> on the page and verifies each has an accessible name from one of:
- An explicit
<label for="id"> - A wrapping
<label>…<input>…</label> - An
aria-label="…"attribute - An
aria-labelledby="other-id"pointing at visible text - A
titleattribute (worst — a hover tooltip is an inferior fallback, but it counts)
The check fails when an input has none of these and is not a hidden field, submit button, or csrf token.
Why it matters
A screen-reader user filling out a checkout form hears “edit text, edit text, edit text” instead of “card number, expiry, CVV.” Unlabeled fields are unusable.
- Conversion impact, not just compliance. Every UX study on payment forms shows the same thing: visible field labels (not placeholder-only) lift completion rates 10–20%. Placeholders disappear once you start typing — your user can’t double-check what they’re typing into.
- Legal exposure. Form labels are one of the most commonly cited WCAG failures in accessibility lawsuits because they’re easy to verify (open the page in NVDA, tab through). Most ADA Title III complaints filed against US ecommerce sites cite form labels among the top three violations.
- Autofill works better. Browsers and password managers use label text to detect field types. A labeled “Email” field gets autofilled with the email; an unlabeled
<input type="text">doesn’t.
How to fix it
Use a visible <label> connected to the input by for/id. Don’t rely on placeholder text alone.
The canonical pattern:
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email" required>
Wrapping pattern (label as parent, no for needed):
<label>
Email address
<input name="email" type="email" autocomplete="email" required>
</label>
Icon-only buttons (no visible text) — use aria-label:
<button type="submit" aria-label="Search">
<svg aria-hidden="true">…</svg>
</button>
React with controlled inputs:
<label htmlFor="email">Email address</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="email"
required
/>
(Note: JSX uses htmlFor, not for — for is a reserved word.)
Floating labels (Material Design, shadcn/ui) — the visible label is animated, but the underlying HTML must still pair <label for> with <input id>. If your design system hides the label visually, use class="sr-only" (a Tailwind utility) instead of dropping the element:
<label for="search" class="sr-only">Search the docs</label>
<input id="search" type="search" placeholder="Search…">
Don’t.
- Don’t use placeholder as the only label — it disappears once typing starts and has lower contrast.
- Don’t use
<div>or<span>as a label — those don’t expose the association to assistive tech. - Don’t reuse the same
idon multiple inputs — the association breaks silently.
Linters and CI. eslint-plugin-jsx-a11y ships a label-has-associated-control rule. Add it to your config and fail CI on missing labels. axe-core / Playwright can catch them at runtime — see the snippet on the Lighthouse accessibility score page.
Frequently asked questions
Is placeholder a label?
No. Placeholders are hints for empty fields; they vanish on focus. WCAG explicitly states placeholders do not satisfy the labels requirement.
What about aria-label?
aria-label works, but visible labels work better — sighted users with cognitive disabilities, low-vision users with magnifiers, and users translating the page in their browser all benefit from text they can see. Use aria-label only when a visible label would be redundant (icon-only buttons, search inputs with a clear submit icon).
Do I need a label for submit buttons?
<button type="submit">Sign up</button> has its own visible text — that is the accessible name, no extra label needed. Same for <input type="submit" value="Sign up">. Icon-only submit buttons need aria-label.
Sources
Last updated 2026-05-11