A form error needs to identify the field, state what failed, and offer a way to correct it. A visible label gives a control its name; help and error text can be associated using aria-describedby, and aria-invalid marks a field after validation fails. An error summary can collect failures and receive focus after a server response so a keyboard user knows what happened. A status message can announce a successful save without moving focus. These features support the form's actual behavior; they cannot repair missing labels or a disabled submit path. The static markup below shows a server-rendered invalid state. A successful initial render should omit the error state until there is an error.
Accessible forms: connect labels, errors, and focus
Case study
An inspector submits case 47 with an empty note. The server returns the form, preserves case 47, and renders an error summary. Focus moves to that summary; its link points to the note control. The field has a persistent label, a length hint, an error message, and aria-invalid. The user can tab to the note and correct it. Red text alone would fail a user who cannot distinguish that color, and a toast that disappears before screen-reader announcement could hide the only explanation. The summary should include every invalid field on a longer form.
Working contract
<main>
<h1>Record inspection</h1>
<div id="error-summary" tabindex="-1" role="alert">
<h2>Check the inspection note</h2>
<p><a href="#inspection-note">Enter a note before saving.</a></p>
</div>
<form action="/inspections" method="post">
<label for="inspection-note">Inspection note</label>
<p id="note-help">Describe the work in 160 characters or fewer.</p>
<p id="note-error">A note is required.</p>
<textarea id="inspection-note" name="note" maxlength="160"
aria-describedby="note-help note-error" aria-invalid="true"></textarea>
<button type="submit">Save inspection</button>
</form>
</main>
<script>document.querySelector("#error-summary").focus();</script>Cost and tradeoffs
Rendering one error per invalid field takes O(F) time and DOM nodes for F fields. Associating nodes by ID is a constant-size markup cost per field. A client-side validator that traverses all controls also takes O(F), but the server still validates the submitted payload. Accessibility checks require keyboard use and assistive-technology testing, not only inspection of attributes. Moving focus on every keystroke would be disruptive; focus the summary once after the failed submission is rendered. Keep the error text in normal reading order and do not hide it behind a hover-only control.
Common Mistakes
- Do not use placeholder text as the only field label.
- Do not report an error only through color or an icon.
- Do not set aria-invalid without explaining how to fix the value.
- Do not move focus repeatedly while the user is editing a field.
Continue through the stack
HTML labels: bind each control to a durable accessible name; HTML form help: connect visible instructions and errors to a control; HTML status region: announce a changed result without stealing focus; Form submission: validate on the server and return field errors.
Failure trace
A submission fails validation, but errors appear only as red borders. A keyboard user remains at the submit button with no indication of which field needs work; a screen reader gets no status change. Return field errors, connect them to their controls, and move focus to a useful summary or first invalid control. Keep success and loading text in an existing status region.
Verification
- Submit with two invalid fields using only the keyboard and locate both messages without guessing.
- Inspect the input's accessible name and error description after failure.
- Trigger a successful save and confirm the status is announced without unexpectedly moving focus.
Decision note
An announced status should describe the state change briefly. Repeated or assertive announcements can interrupt other work; use the least disruptive behavior that still makes the result available.
Further connections
Accessible Names and Native Controls; Dialog Focus and Interruption Boundary; Form Errors and Recovery Paths.
Further connections
Route Scroll and Focus Restoration.
Further connections
Combobox Suggestions and Active Option; Async Status Announcements and Focus.
