Skip to Content
Component ExamplesError Handling

Error Handling & Validation

Inline field errors, form-level error summaries, and accessible validation timing

Maintained by Martin KrugerLast reviewed

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
  • Choose one announcement strategy: focus a headed summary or update a concise live message; avoid duplicate channels
  • 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
View inaccessible code
<!-- 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" />
Accessible
Please enter a valid email address.
View accessible code
<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"> Please enter a valid email address. </span>

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

Expected semantics on focus:

VersionProgrammatic result
Inaccessible (color only)The field has a name and value, but no invalid state or error description.
Accessible (described error)The field exposes its name, invalid state, value, and associated correction text.

Form Error Summary

Inline-Only Errors vs. Error Summary with Links

Inaccessible
View inaccessible code
<!-- 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>
Accessible
View accessible code
<form onsubmit="validateForm(event)"> <!-- Focus one headed summary; inline errors remain descriptions. --> <div id="error-summary" 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"></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"></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>

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

Expected behavior after submit:

VersionFocus and semantic result
Inaccessible (inline only)Focus stays on Submit and the new error text is not associated with its fields.
Accessible (summary + inline)Focus moves to the headed summary; its links identify each field, and every invalid field exposes its own description.

Error Prevention for Important Actions

WCAG 3.3.4 applies to submissions that create legal commitments or financial transactions, modify or delete user-controlled data, or submit test responses. At least one safeguard must exist: make the action reversible, check the data and allow correction, or provide a review-and-confirm step.

Immediate Irreversible Deletion vs. Review and Recovery

Inaccessible

Delete the account and all saved projects.

View inaccessible code
<button onclick="deleteAccountImmediately()">Delete account</button>
Accessible

Review account deletion

This removes 12 projects and ends the subscription. You can restore the account for 30 days.

View accessible code
<section aria-labelledby="review-title"> <h3 id="review-title">Review account deletion</h3> <p>This removes 12 projects. You can restore the account for 30 days.</p> <button>Confirm deletion</button> <button>Go back and correct</button> </section>

Re-validation must clear stale aria-invalid and descriptions when a field becomes valid. Security-sensitive forms may limit how much detail they expose, but should still identify an error and provide a safe correction path.


Resources