WCAG 2.2 Form Compliance Checklists
WCAG 2.2 form compliance comes down to a small, concrete set of Success Criteria that govern how a form identifies errors, labels its controls, suggests corrections, prevents costly mistakes, and announces status — each mappable to a specific ARIA attribute, DOM structure, or interaction you can audit. This guide turns those criteria into actionable checklists you can run against any form, with the exact ARIA and DOM wiring each one requires and how to verify it.
The criteria below are the ones that actually bite on data-entry forms: 1.4.1 Use of Color, 3.3.1 Error Identification, 3.3.2 Labels or Instructions, 3.3.3 Error Suggestion, 3.3.4 Error Prevention, 4.1.3 Status Messages, plus the focus-order and focus-visibility criteria in the 2.4.x family. Every checklist maps back to the house pattern — <form novalidate> rendering its own accessible errors — and to the renderer contract documented across UX Patterns & Error State Design and the Constraint Validation API Deep Dive.
How to Read These Checklists
Each section states the Success Criterion and its conformance level, gives a checklist of - [ ] items you can copy into a review ticket, shows the ARIA/DOM wiring that satisfies it, and names the audit method. The criteria are not independent: a single invalid field touches 3.3.1, 3.3.3, 1.4.1, and 4.1.3 at once, which is why the house renderer sets aria-invalid, links a role="alert" message that names a fix, adds a non-color cue, and updates a live region in one pass.
Prerequisites
| Tool | Role in the audit |
|---|---|
| Keyboard only | Verifies focus order (2.4.3), focus visibility (2.4.7/2.4.11), and that errors are reachable |
| Screen reader (NVDA/VoiceOver) | Confirms 3.3.1 identification and 4.1.3 status messages are announced |
| Color contrast checker | Verifies 1.4.1 cues and 1.4.3 text contrast (4.5:1) |
| Automated scanner | axe-core catches association and contrast regressions — see axe-core accessibility testing |
| Interaction tests | Playwright asserts ARIA state and focus on real submissions — see Playwright form validation testing |
Success Criterion Reference
| SC | Name | Level | Primary form mechanism |
|---|---|---|---|
| 1.4.1 | Use of Color | A | Non-color error cue (icon + text) |
| 3.3.1 | Error Identification | A | aria-invalid + associated text message |
| 3.3.2 | Labels or Instructions | A | <label for>, aria-describedby hints |
| 3.3.3 | Error Suggestion | AA | Message names the correction |
| 3.3.4 | Error Prevention | AA | Review/confirm/reverse for legal/financial data |
| 4.1.3 | Status Messages | AA | aria-live region announces without focus change |
| 2.4.3 | Focus Order | A | DOM order matches visual/logical order |
| 2.4.7 | Focus Visible | AA | Visible focus indicator on every control |
| 2.4.11 | Focus Not Obscured (Min) | AA | Focused control not fully hidden by sticky UI |
SC 1.4.1 — Use of Color (Level A)
Color must never be the only way error state is conveyed. A red border with no text, icon, or aria-invalid is a 1.4.1 failure for color-blind and screen-reader users alike.
<!-- Color is reinforced by an icon and associated text, not used alone. -->
<input id="email" type="email" aria-invalid="true" aria-describedby="email-error" />
<p id="email-error" role="alert" class="field-error">
<svg aria-hidden="true" class="error-icon" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
Enter your email address.
</p>
Audit: grayscale the form (devtools rendering emulation) and confirm every error is still distinguishable. Automated contrast checks live in axe-core accessibility testing.
SC 3.3.1 — Error Identification (Level A)
When input is rejected, the field in error must be identified and the error described in text. This is the criterion native tooltips fail, because they are inconsistently exposed to assistive technology — the reason the house pattern suppresses them with novalidate.
function identifyError(input: HTMLInputElement, message: string): void {
const errorId = `${input.id}-error`;
const error = document.getElementById(errorId)!;
error.textContent = message;
error.setAttribute('role', 'alert');
input.setAttribute('aria-invalid', 'true');
// Preserve any existing hint id when adding the error id.
input.setAttribute('aria-describedby', `${input.id}-hint ${errorId}`.trim());
}
Audit: with a screen reader, submit an invalid form and confirm each error is announced and the field is reachable. The dedicated WCAG 3.3.1 error identification checklist covers this criterion in depth, and the reading ValidityState flags for granular errors recipe shows how to derive a precise message per failure.
SC 3.3.2 — Labels or Instructions (Level A)
Every control that takes input needs a label or instruction so users know what to enter and in what format.
<label for="password">Password</label>
<input id="password" type="password" required minlength="8"
aria-describedby="password-hint" />
<span id="password-hint" class="hint">At least 8 characters, including a number.</span>
Audit: tab through the form with a screen reader and confirm each control announces a name and its instructions. axe-core flags unlabeled controls automatically.
SC 3.3.3 — Error Suggestion (Level AA)
When the system knows a likely correction, the error message must suggest it — “Enter a date in DD/MM/YYYY format”, not “Invalid date”.
function suggestForEmail(input: HTMLInputElement): string {
if (input.validity.valueMissing) return 'Enter your email address.';
if (input.validity.typeMismatch) return 'Enter an email like name@example.com.';
return '';
}
Audit: trigger each constraint and confirm the message tells the user how to fix it. The WCAG 3.3.3 error suggestion patterns page provides the full pattern catalogue, and the messaging style draws on inline error messaging strategies.
SC 3.3.4 — Error Prevention (Legal, Financial, Data) (Level AA)
For submissions that carry legal commitments, financial transactions, or modify user data, at least one of reversible, checked, or confirmed must hold.
Audit: walk the high-stakes flow end to end and confirm a review/confirm or undo path exists. This is a process audit, not an attribute check.
SC 4.1.3 — Status Messages (Level AA)
Status messages — “3 errors found”, “Email available”, “Saving…” — must be announced by assistive technology without moving focus. That requires a live region.
<!-- Pre-rendered, empty, so injected text is announced. -->
<div id="form-status" aria-live="polite" class="visually-hidden"></div>
function announce(message: string): void {
const region = document.getElementById('form-status')!;
region.textContent = ''; // reset so identical repeated messages still fire
requestAnimationFrame(() => (region.textContent = message));
}
Audit: with a screen reader, submit the form and trigger async checks; confirm counts and availability results are spoken without focus jumping. Live-region timing on WebKit is occasionally delayed, so verify in the Playwright form validation testing WebKit project.
Focus Criteria (2.4.3, 2.4.7, 2.4.11)
Error handling routes focus, so the 2.4.x focus criteria apply directly.
Audit: keyboard-only, submit an invalid form and confirm focus is visible, on the right element, and not obscured. The runtime behavior is documented in Focus Management & Keyboard Navigation and the recipe on managing focus after validation failure.
Common Gotchas
Gotcha 1 — Color-only error state
<!-- BEFORE: red border only — fails 1.4.1 -->
<input id="zip" class="invalid" />
<!-- AFTER: color + icon + associated text -->
<input id="zip" aria-invalid="true" aria-describedby="zip-error" class="invalid" />
<p id="zip-error" role="alert">Enter a 5-digit ZIP code.</p>
Gotcha 2 — Placeholder used as the label
<!-- BEFORE: vanishes on input, low contrast — fails 3.3.2 -->
<input type="email" placeholder="Email" />
<!-- AFTER: real label, placeholder optional -->
<label for="em">Email</label>
<input id="em" type="email" placeholder="name@example.com" />
Gotcha 3 — Generic message that suggests nothing
// BEFORE: fails 3.3.3
return 'Invalid input.';
// AFTER: names the fix
return 'Enter a date in DD/MM/YYYY format.';
Gotcha 4 — Status injected into a region added at the same time
// BEFORE: region created and filled together — often not announced
const r = document.createElement('div');
r.setAttribute('aria-live', 'polite');
r.textContent = '3 errors found';
document.body.append(r);
// AFTER: region pre-rendered empty, then filled — reliably announced
announce('3 errors found');
Browser & Assistive-Tech Compatibility Matrix
| Mechanism | Chrome + NVDA | Firefox + NVDA | Safari + VoiceOver |
|---|---|---|---|
aria-invalid announced |
Yes | Yes | Yes |
aria-describedby message read |
Yes | Yes | Yes |
aria-live="polite" status |
Reliable | Reliable | Occasionally delayed |
role="alert" interruption |
Reliable | Reliable | Reliable |
Visible focus on programmatic focus() |
Yes | Yes | Yes |
Frequently Asked Questions
Which WCAG 2.2 criteria matter most for form validation?
3.3.1 Error Identification, 3.3.2 Labels or Instructions, 3.3.3 Error Suggestion, 3.3.4 Error Prevention, 4.1.3 Status Messages, and 1.4.1 Use of Color, plus the 2.4.x focus criteria that govern where focus lands after a failed submit.
Why do native validation tooltips fail SC 3.3.1?
Native tooltips are inconsistently exposed to screen readers, are not present in the DOM as text, and
vanish on interaction. Suppress them with novalidate and render your own messages using
aria-invalid, aria-describedby, and a live region.
How do I announce an error count without moving focus?
Put the count in a pre-rendered aria-live="polite" region and update its text on submit.
Reset the text to empty first so an identical repeated count still triggers an announcement. That
satisfies SC 4.1.3 without stealing focus.
Does every form need SC 3.3.4 Error Prevention?
3.3.4 applies specifically to submissions that are legal, financial, or modify/delete user data. For those, the action must be reversible, checked for errors, or confirmed via a review step. Low-stakes forms still benefit from a confirm step but are not bound by this criterion.
Can automated tools confirm WCAG form conformance on their own?
No. Scanners like axe-core catch missing labels, broken associations, and contrast failures, but 3.3.3 suggestion quality and 3.3.4 review flows need manual and screen-reader review. Combine automated scans with keyboard and assistive-technology audits.
Related Guides
- WCAG 3.3.1 Error Identification Checklist — focused checklist and implementation for identifying errors
- WCAG 3.3.3 Error Suggestion Patterns — patterns for suggesting corrections
- axe-core Accessibility Testing — automate the contrast and association checks in CI
- Playwright Form Validation Testing — assert ARIA state and focus on real submissions
- Focus Management & Keyboard Navigation — satisfy the 2.4.x focus criteria
← Back to Testing & Accessibility