Form errors: accessible summary + inline messages
Goal
When a form submission fails, users should quickly learn what went wrong, where it went wrong, and how to fix it — regardless of input method (mouse, keyboard, screen reader, voice).
Checklist
- Don’t rely on color alone: show a text error message, not just a red border.
- Use an error summary for submit-time validation: especially for long forms or when multiple fields can fail.
- Move focus to the summary on submit: give global context first. Use
tabindex="-1"on the summary container. - Link summary items to fields: each error should be clickable and move focus to the relevant control.
- Mark invalid fields: set
aria-invalid="true"on invalid controls. - Connect the message to the control: use
aria-describedbyto reference the error message element. - Keep IDs stable: error message elements should have consistent IDs (avoid regenerating new IDs each render).
- Announce intentionally: use
role="alert"for the summary (submit-time) orrole="status"for gentle updates. Avoid multiple competing live regions.
Minimal pattern sketch
<div id="errorSummary" hidden role="alert" aria-labelledby="errTitle" tabindex="-1">
<h2 id="errTitle">Please fix the following</h2>
<ul>
<li><a href="#email">Email: Enter your email.</a></li>
<li><a href="#zip">ZIP: Enter a 5-digit ZIP code.</a></li>
</ul>
</div>
<label for="email">Email</label>
<input id="email" aria-invalid="true" aria-describedby="emailError">
<p id="emailError">Enter your email.</p>
Common pitfalls
- Inline-only errors without a summary: users may never discover the first failing field, especially on long forms.
- Focusing the first invalid field immediately: this can be disorienting (no global context). Many teams prefer focusing the summary first.
- Overusing
role="alert": it can be interruptive; reserve it for submit-time failures or critical system states. - Clearing errors too aggressively: don’t remove context while the user is still reading. Clear when the field becomes valid or on resubmit.