Overview
Accessible error handling ensures that all users — including screen reader users and those with low vision — can identify, understand, and correct form errors. Errors communicated only through color are invisible to many users. Error messages that are not programmatically associated with their fields are never announced on focus. A well-structured error summary gives users a roadmap to fix all problems at once.
WCAG Criteria:
- 3.3.1 Error Identification — errors must be identified and described in text
- 3.3.3 Error Suggestion — suggestions for correction must be provided when known
- 1.4.1 Use of Color — color must not be the only visual means of conveying information
- 4.1.3 Status Messages — status updates must be announced without moving focus
Key requirements:
- Never use color alone to indicate an error (WCAG 1.4.1) — always include a text message
- Use
aria-invalid="true"on the field andaria-describedbypointing to the error message role="alert"containers must exist in the DOM before content is injected into themaria-errormessagehas incomplete browser support — usearia-describedbyas the primary association- Validate inline on blur for individual fields; validate on submit for first-pass validation
- Error summaries should list all errors with links to the corresponding fields
Inline Field Error
Color-Only Error vs. Described Error with aria-invalid
Inaccessible
<!-- Only a red border — no text message, no aria attributes -->
<label for="error-handling-bad-email">Email</label>
<input
id="error-handling-bad-email"
type="text"
value="not-an-email"
style="border-color: red"
/>Live Preview
Accessible
<label for="error-handling-good-email">Email</label>
<input
id="error-handling-good-email"
type="text"
value="not-an-email"
aria-invalid="true"
aria-describedby="error-handling-good-email-error"
/>
<span id="error-handling-good-email-error" role="alert">
Please enter a valid email address.
</span>Live Preview
Please enter a valid email address.
What’s wrong with color-only errors?
- The red border is invisible to screen reader users — no error is ever announced
- Users with color vision deficiencies may not distinguish the red border from a normal one
- There is no
aria-invalidattribute, so assistive technology does not know the field is in an error state - No text message exists to describe what went wrong or how to fix it
What the screen reader announces:
| Version | Announcement on focus |
|---|---|
| Inaccessible (color only) | “Email, edit text, not-an-email” — no error mentioned |
| Accessible (described error) | “Email, invalid entry, edit text, not-an-email — Please enter a valid email address.” |
Form Error Summary
Inline-Only Errors vs. Error Summary with Links
Inaccessible
<!-- No error summary — errors appear inline with no announcement -->
<form onsubmit="showErrors()">
<label for="name">Name</label>
<input id="name" type="text" />
<p class="error" style="color:red; display:none">
Name is required.
</p>
<label for="email">Email</label>
<input id="email" type="text" />
<p class="error" style="color:red; display:none">
Please enter a valid email.
</p>
<button type="submit">Submit</button>
</form>Live Preview
Accessible
<form onsubmit="validateForm(event)">
<!-- Summary container exists in DOM before errors are injected -->
<div id="error-summary" role="alert" tabindex="-1" hidden></div>
<label for="error-handling-good-name">Name</label>
<input
id="error-handling-good-name"
type="text"
aria-describedby="name-err"
/>
<span id="name-err" role="alert"></span>
<label for="error-handling-good-sum-email">Email</label>
<input
id="error-handling-good-sum-email"
type="text"
aria-describedby="email-err"
/>
<span id="email-err" role="alert"></span>
<button type="submit">Submit</button>
</form>
<script>
function validateForm(e) {
e.preventDefault();
// 1. Populate the summary with linked errors
summary.innerHTML = `
<h3>2 errors found</h3>
<ul>
<li><a href="#name">Name: required</a></li>
<li><a href="#email">Email: invalid</a></li>
</ul>`;
summary.hidden = false;
summary.focus(); // move focus to summary
// 2. Mark fields invalid
nameInput.setAttribute('aria-invalid', 'true');
// 3. Inject inline error text
nameErr.textContent = 'This field is required.';
}
</script>Live Preview
What’s wrong without an error summary?
- After submission, sighted users may see red text but screen reader users get no notification that errors occurred
- Without
role="alert"on a pre-existing container, the error messages are not announced - There is no way for a screen reader user to get an overview of all errors at once
- Inline errors without
aria-invalidoraria-describedbyare not associated with their fields
What the screen reader announces:
| Version | Announcement after submit |
|---|---|
| Inaccessible (inline only) | Nothing — focus stays on submit button, no errors announced |
| Accessible (summary + inline) | “2 errors found. Name: This field is required. Email: Please enter a valid email address.” — then each field announces its error on focus |