Skip to content

Tutorial

How to make form errors accessible

2 min read

In short

A form error has to be named in text (3.3.1), tied to its field programmatically (aria-describedby plus aria-invalid), and say how to get it right (3.3.3). If it appears only after submission it also has to be announced: role="alert", or move focus into an error summary (4.1.3).

At a glance

  1. Write the error as text

    Colour and borders are not enough. The error needs a sentence that names it.

  2. Tie it to the field

    Point aria-describedby at the message id and set aria-invalid="true" on the field.

  3. Say the way out

    Write what is expected, with an example: „Example: name@company.com“.

  4. Make sure it is announced

    For errors that appear later, use role="alert" or move focus into the error summary.

Forms are where accessibility turns into revenue: someone who cannot find the error does not order. And this is exactly where most implementations fail, because the error message exists for eyes only.

What goes wrong here

Before: the error exists visually onlyHTML
<form>
  <label for="mail">E-Mail</label>
  <input id="mail" name="mail" class="input-error">
  <!-- Fehler nur farblich und optisch, nicht programmatisch:
       - keine Verbindung zum Feld
       - keine Rolle, also keine Ansage
       - "ungueltig" sagt nicht, was erwartet wird -->
  <span class="error-text">Ungueltig</span>

  <button type="submit">Weiter</button>
</form>
  • The message is not tied to the field: someone tabbing into it hears nothing about it.
  • „Invalid“ does not say what is expected (3.3.3 asks for a suggestion when one is known).
  • The message appears silently: with no live semantics and no focus change, nobody who is not looking notices (4.1.3).

The fix on a single field

After: named, associated, explainedHTML
<form novalidate>
  <label for="mail">E-Mail</label>
  <input
    id="mail"
    name="mail"
    type="email"
    autocomplete="email"
    aria-invalid="true"
    aria-describedby="mail-error"
  >
  <!-- Mit dem Feld verbunden (aria-describedby), als Text identifiziert
       und mit einem konkreten Hinweis, wie es richtig geht -->
  <p id="mail-error" class="error-text">
    E-Mail-Adresse fehlt das @-Zeichen. Beispiel: name@firma.de
  </p>

  <button type="submit">Weiter</button>
</form>

Several errors at once

Error summary with a focus moveHTML
<!-- Bei mehreren Fehlern: eine Zusammenfassung oben, fokussiert nach
     dem Absenden. Jeder Eintrag springt zum betroffenen Feld. -->
<div role="alert" tabindex="-1" id="error-summary">
  <h2>2 Angaben fehlen</h2>
  <ul>
    <li><a href="#mail">E-Mail-Adresse fehlt das @-Zeichen</a></li>
    <li><a href="#plz">Postleitzahl braucht 5 Ziffern</a></li>
  </ul>
</div>

<script>
  // Nach dem Absenden: Fokus in die Zusammenfassung, damit die Ansage
  // ankommt und die Tastatur direkt am Fehler steht.
  document.getElementById("error-summary").focus();
</script>

After submitting a long form, the summary is the most reliable route: it states how many errors there are, names each one, and takes focus to the right place in one click. That helps everyone, not only screen-reader users.

Common additional mistakes

  • Placeholder as a label: it disappears while typing and is not a label (use a real label element).
  • Error colour with no second cue: red alone violates 1.4.1 (Use of Colour).
  • Messages that vanish and reappear on every keystroke: that produces one announcement per key. Validate on blur or on submit.
  • An aria-live container inserted together with its text: often nothing is heard. The container has to exist first and only be filled.

Testing

  1. Submit the form empty and navigate to the first error with the keyboard only.
  2. Check with a screen reader: is the error announced? Is it repeated when the field takes focus?
  3. Zoom to 200 percent: does the message stay visible next to its field?

Frequently asked questions

Is the browser's built-in HTML validation enough?
It is a good starting point, but its messages are barely styleable, worded differently per browser, and disappear by themselves. For a reliable result, validate yourself and write the message into the markup.
When role="alert" and when a focus move?
role="alert" suits single short messages that must arrive immediately. For several errors after submission, moving focus into a summary is more reliable, because it also takes the operating position along.
Does every error message need a suggestion?
3.3.3 asks for one when it is known and would not compromise security. For a format error it is known („Example: name@company.com“); for a login password it is not, and there it would be harmful.

Request a free scan

Tell us your domain - we run a free initial scan and show you the most important barriers.