Building a form that takes input is the easy half. The hard half is what happens
when the input is wrong — or when the submission is one a person can’t safely take
back. Error recovery is where accessibility, usability, and legal risk all meet: a
rejected field that never says which field, a “please fix the errors”
message that never says how, or a one-click purchase with no way to review
before it commits. Each of these turns an ordinary mistake into a dead end, and the
people it strands first are those using screen readers, magnification, or working
under cognitive load.
This lesson works through three defects in the error-recovery path, all drawn from
WCAG’s Input Assistance guideline. They build on one another: identify the
error in text, then suggest how to fix it, then — for consequential actions — give
the user a chance to review, confirm, or undo before the form commits.
What you’ll learn
How to identify a field in error in text and tie that text to the
field (3.3.1); how to tell the user how to correct the value when a fix is known,
without leaking anything that would weaken security (3.3.3); and how to give a
review, confirmation, or undo step before a legal, financial, or data-changing
submission becomes final (3.3.4).
Standards this lesson maps to
Standard
Criterion
Level
What it requires
WCAG 2.2
3.3.1 Error Identification
A
If an input error is detected automatically, the item in error is identified and described to the user in text.
WCAG 2.2
3.3.3 Error Suggestion
AA
If an error is detected and a correction is known, the suggestion is provided, unless it would jeopardise security or the purpose of the content.
WCAG 2.2
3.3.4 Error Prevention (Legal, Financial, Data)
AA
For legal commitments, financial transactions, or data changes, submissions are reversible, checked for errors, or confirmed before they finalise.
WCAG 2.2
4.1.3 Status Messages
AA
Error and status messages are exposed to assistive technology without moving focus.
EN 301 549
9.3.3 (incorporates WCAG)
—
European harmonised standard; references the WCAG A/AA set including the Input Assistance criteria.
Section 508
502.3 / 504 (incorporates WCAG A & AA)
—
US federal ICT must meet WCAG 2.0 Level A and AA, including error identification, suggestion, and prevention.
ADA Title II
WCAG 2.1 AA (DOJ rule)
AA
US state/local government web content must conform to WCAG 2.1 AA, which includes 3.3.1, 3.3.3, and 3.3.4.
The three problems we’ll fix
Each card below isolates one common error-recovery defect. For every issue you get a
plain-language statement of the problem, a Bad example (shown as
escaped, non-running code so it can’t affect this page), a Good
example, the copyable Code, and an ordered fix.
Error not identified in text or associated with its field
WCAG 2.2 · 3.3.1A4.1.3AAEN 301 549Section 508
When validation fails, the user has to learn two
things: which field is wrong and what is wrong with it — both in
text. A red border, a coloured outline, or a small icon conveys neither to a screen
reader, and colour alone also fails low-vision and colour-blind users. The error
text must exist as words, be tied to the field with
aria-describedby, and the field must carry
aria-invalid="true" so its invalid state is exposed. If the error
appears after submit without focus moving, it also needs to be announced (4.1.3).
Bad
The only signal that something is wrong is a red border applied with a class.
There is no text, no association, and nothing exposed to assistive technology.
bad-error-identify.html
<label for="zip">ZIP code</label>
<input type="text" id="zip" name="zip" class="is-invalid">
<!-- .is-invalid only paints a red border; no text, no link to the field -->
Good
The error is real text, linked to the field with
aria-describedby, and the field is marked
aria-invalid="true". The message names the problem, not just the
fact of failure.
good-error-identify.html
<label for="zip">ZIP code</label>
<input type="text" id="zip" name="zip"
aria-describedby="zip-error" aria-invalid="true">
<p class="error" id="zip-error">
<span class="error__icon" aria-hidden="true">⚠</span>
ZIP code is required.
</p>
Code
On submit, gather every failing field into a summary, give it
role="alert", and move focus to it so the failure is announced and
each entry links to the field it names (3.3.1 + 4.1.3).
error-summary.html
<div role="alert" tabindex="-1" id="errors">
<h2>There are 2 problems</h2>
<ul>
<li><a href="#zip">ZIP code is required.</a></li>
<li><a href="#email">Enter a valid email address.</a></li>
</ul>
</div>
<!-- After render: document.getElementById('errors').focus(); -->
How to fix
Describe every detected error in text that names the field and the problem;
never rely on colour, border, or icon alone.
Link the message to its field with aria-describedby and set
aria-invalid="true" while the field is in error.
On submit, build an error summary, link each item to its field, and move
focus to the summary so the failure is announced (4.1.3).
Remove aria-invalid and the message once the value becomes
valid, so stale errors aren’t announced.
No suggestion for how to fix the error
WCAG 2.2 · 3.3.3AAEN 301 549ADA Title II
“Invalid input” tells the user they failed but not how
to succeed. When you can detect why a value was rejected, Error Suggestion
asks you to say what a correct value looks like: the required format, the allowed
range, the missing piece, or a valid example. This matters most for people with
cognitive disabilities, who may not infer the rule from a generic rejection. The one
exception is security: if naming the fix would weaken it — for example, confirming
which half of a username/password pair was wrong — you may withhold the suggestion.
Bad
The message confirms failure and stops there. The user is told the date is
invalid but not what format the field expects.
The message states the expected format and gives a concrete example, so the
user can correct the value without guessing. The instruction is also shown up
front, before any error occurs.
good-suggestion.html
<label for="dob">Date of birth</label>
<input type="text" id="dob" name="dob"
aria-describedby="dob-hint dob-error" aria-invalid="true">
<p class="hint" id="dob-hint">Use the format DD/MM/YYYY.</p>
<p class="error" id="dob-error">
Enter your date of birth as DD/MM/YYYY, for example 09/04/1990.
</p>
Code
Tailor the suggestion to the actual failure, and recognise the security
exception: on a sign-in form, keep the message generic so it doesn’t reveal
which credential was wrong.
suggestion-by-reason.html
<!-- Specific, because the rule is safe to share -->
<p class="error">Password must be at least 8 characters. You entered 5.</p>
<!-- Range suggestion -->
<p class="error">Enter a quantity between 1 and 20.</p>
<!-- Security exception: stay generic on sign-in -->
<p class="error">That username and password don’t match. Try again.</p>
How to fix
When you know why a value failed, say what a correct value looks like — a
format, a range, a missing element, or a valid example.
Show format instructions up front as a hint as well, so users avoid the
error in the first place.
Match the suggestion to the actual failure rather than reusing one generic
message for every case.
Withhold the suggestion only where revealing it would jeopardise security
(such as which sign-in credential was wrong).
Legal or financial submit with no review, confirm, or undo
WCAG 2.2 · 3.3.4AAEN 301 549Section 508ADA Title II
Some submissions can’t simply be tried again — placing
an order, transferring money, accepting terms, deleting an account, or casting a
vote. Error Prevention says that for legal commitments, financial transactions, or
changes to stored data, at least one safety net must be present: the action is
reversible, the data is checked for errors with a chance to
correct, or the user confirms before it finalises. A slip from a screen
reader user, a mis-tap on a touch target, or a double submit shouldn’t cost real
money. Meeting any one of the three options satisfies the criterion.
Bad
A single button commits the payment immediately. There is no review screen, no
confirmation, and no way to undo — one click is final.
bad-instant-submit.html
<form action="/charge" method="post">
<!-- amount and card fields … -->
<button type="submit">Pay £499 now</button>
</form>
<!-- Submits and charges in one step: no review, confirm, or undo -->
Good
A review step shows what will happen and asks for explicit confirmation before
the charge is made. The user can go back and edit, and the confirm control is
separate from the form’s normal submit.
good-review-confirm.html
<h1>Review your order</h1>
<dl>
<dt>Item</dt><dd>Annual plan</dd>
<dt>Total</dt><dd>£499.00</dd>
<dt>Card</dt><dd>Visa ending 4242</dd>
</dl>
<a href="/checkout">Edit details</a>
<form action="/charge" method="post">
<label><input type="checkbox" name="confirm" required>
I confirm this £499 charge</label>
<button type="submit">Confirm and pay</button>
</form>
Code
Any one of the three mechanisms is enough. Reversible covers many cases:
provide an undo window, or a clear cancellation path, instead of a confirm step.
reversible-undo.html
<!-- Option 1: reversible — an undo window after the action -->
<div role="status">
Account deleted.
<a href="/undo-delete?token=…">Undo (available for 14 days)</a>
</div>
<!-- Option 2: checked — re-validate and show a correction step -->
<!-- Option 3: confirmed — explicit confirm before finalising -->
How to fix
Identify submissions that are legal commitments, financial transactions, or
changes to or deletions of the user’s stored data.
Provide at least one safety net: make the action reversible, check the data
with a chance to correct, or require explicit confirmation before it commits.
For a confirm step, show a review of exactly what will happen and a clear way
to go back and edit.
For a reversible action, offer a real undo or cancellation window and tell the
user how long they have.
Make the safety net itself accessible — keyboard reachable, announced, and not
time-pressured beyond what 2.2.1 allows.
Recap
When a field is rejected, name it and describe the problem in text — not by
colour or position alone — and tie that text to the field with
aria-describedby and aria-invalid (3.3.1).
When you know how to fix the value, say so: a format, an allowed range, or a
valid example. Hold the suggestion back only where revealing it would weaken
security (3.3.3).
For legal, financial, or data-changing actions, make the submission reversible,
add a checking step, or require explicit confirmation before it commits (3.3.4).
Identify, suggest, then prevent — the same three steps satisfy
WCAG, EN 301 549, Section 508, and ADA Title II together.