Understanding WCAG 3.3.1

What is it?

When a form rejects what you typed, you need to know three things:

  1. That an error occurred
  2. Where the error is
  3. What the error is

…in text, and ideally programmatically tied to the input, not just a red form field or an alert icon.

Why it matters

Sighted users can see a red border and understand “this is where the problem is.” Everyone else gets nothing.

A form that doesn’t tell you what went wrong is a form you can’t finish.

Users give up, abandon the task, or have to ask for help with something that should have taken a few seconds.

Who it affects

People who…

Common Failures

1. Color-only error indication

A red border, background, or icon with no text. Users who can’t perceive color can’t tell anything is wrong.

(This also fails 1.4.1 Use of Color.)

2. Error text visible but not tied to the input

Screen reader users hear the label but no error message when the input gets focus. The visible “Email address is required” text exists on the page, but there’s no aria-describedby connection between the input and the message.

3. Generic “Please fix the errors below”

The form summary says something is wrong but doesn’t say what or where. Users have to guess what the problem is. In a 40-field form, this is its own special kind of cruelty.

4. Icons without alternative text

A red X near the input, with no accessible name. Sighted users see the icon. Screen readers hear only “Email address, edit text” with no indication that anything is wrong.

5. Errors that show up silently after submit

The user presses submit. Error messages appear visually next to the broken fields. Focus stays on the submit button. The screen reader says nothing. Screen reader users won’t know anything changed.

6. Custom validation that drops the semantics

The browser’s built-in semantics are gone without a replacement. Visual styling is applied, but semantic information and focus management are missing.

Example of what NOT to do:

form.addEventListener('submit', e => {
  if (!email.value.trim()) {
    e.preventDefault();
    email.classList.add('error');
    errorMsg.style.display = 'block';
  }
});

No focus change, no announcement of errors for screen readers.

Solutions

Pair every visual cue with a text message

Tie the error to the input

Use aria-describedby to connect the input to its error message.

<label for="email">Email address</label>
<input
  id="email"
  type="email"
  aria-invalid="true"
  aria-describedby="email-error"
  required
>
<p id="email-error" class="error">
  Email address is required. Use the format [email protected].
</p>

Announce errors that appear after submit

If error messages are added to the page after the user submits, put them in a region with role="alert" and/or move keyboard focus to an error summary at the top of the form.

<div role="alert" tabindex="-1">
  <h2>There were 3 problems with your submission</h2>
  <ul>
    <li><a href="#email">Email address is required</a></li>
    <li><a href="#phone">Phone number must be 10 digits</a></li>
    <li><a href="#zip">ZIP must be 5 digits</a></li>
  </ul>
</div>

Each error in the list links directly to the input that needs fixing.

Render the alert container on page load (empty), then populate it on submit. Newly-injected role="alert" elements aren’t announced reliably.

For inline validation, slow down before announcing

Live validation that fires on every keystroke creates a wall of noise for screen reader users. Wait until blur, or until the user has paused typing, before announcing the error.

How to test

Closing

A form that fails silently is a form that gives up halfway. Tell people what went wrong, point to where it went wrong, and give them a clear way out.

3.3.1 isn’t about being polite. It’s about giving every user the same chance to finish what they started.

Learn more

Read the full breakdown.

Want more clear and actionable WCAG breakdowns? Check out WCAG in Plain English.