Skip to Content
Component ExamplesError Handling

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:

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 and aria-describedby pointing to the error message
  • role="alert" containers must exist in the DOM before content is injected into them
  • aria-errormessage has incomplete browser support — use aria-describedby as 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-invalid attribute, 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:

VersionAnnouncement 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-invalid or aria-describedby are not associated with their fields

What the screen reader announces:

VersionAnnouncement 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

Resources