Accessible Forms
debt(d4/e3/b5/t6)
Closest to 'default linter catches the common case' (d3) +1. Tools like axe, Lighthouse, and WAVE can detect missing labels and missing aria-describedby associations automatically. However, they only catch the structural issues (missing for/id, placeholder-as-label). More nuanced problems like error messages not being announced at the right time, or required indicator without explanation, require manual testing with a screen reader. The automated detection is good but incomplete, so d4 sits between d3 (linter catches common case) and d5 (specialist tool).
Closest to 'simple parameterised fix' (e3). The quick_fix describes adding visible labels with for/id, linking errors via aria-describedby, marking required with aria-required, and adding role=alert. Each fix is a small template-level change, but it typically touches multiple form templates across a project — not just a one-line patch. Still, each individual fix is a straightforward pattern replacement within a single component/template, fitting e3.
Closest to 'persistent productivity tax' (b5). Accessible form patterns must be applied to every form across the entire web application. This is a cross-cutting concern that affects every developer who creates or modifies forms. It's not architectural-level (you don't need to rewrite the system), but it's a persistent discipline that slows work if not baked into component libraries or design systems. Every new form, every new validation flow needs to account for these patterns.
Closest to 'serious trap' (t7) -1. The canonical misconception — that placeholder is a sufficient label — is extremely common and directly contradicts what many developers assume from visual appearance. Developers coming from a visual-first mindset naturally think 'the user can see what to type' and don't realize placeholders disappear on focus, have contrast issues, and aren't reliably announced by screen readers. The error-linking trap (not knowing you need aria-describedby) is another significant gotcha. These traps contradict how developers expect HTML forms to 'just work' for accessibility, but they're well-documented and learnable, so t6 rather than t7.
Also Known As
TL;DR
Explanation
Label via for/id or wrapping — never placeholder-only. Errors: aria-describedby links error to input; aria-invalid=true on invalid inputs. Required: required attribute or aria-required. Autocomplete attribute helps password managers. Announce errors with role=alert or aria-live=polite.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Placeholder as label
- Errors not linked to inputs
- Required indicator without explanation
- Validating on every keystroke
Avoid When
- Do not use placeholder text as a substitute for a visible label — placeholders disappear on input and fail contrast requirements.
- Avoid grouping unrelated fields inside a single fieldset — it creates confusing context for screen reader users.
When To Use
- Apply accessible form patterns to every form — not just those explicitly flagged for accessibility review.
- Use aria-describedby to link error messages to their input so screen readers announce the error when focus moves to the field.
- Use aria-live regions for dynamic validation feedback that appears without a page reload.
Code Examples
<input type="email" placeholder="Email">
<span style="color:red">Invalid</span>
<label for="email">Email *</label>
<input id="email" type="email" aria-required="true" aria-invalid="true" aria-describedby="err">
<span id="err" role="alert">Enter a valid email</span>