WCAG 2.2 Reference
Plain-English explanations of every WCAG 2.2 criterion most commonly violated — with real failure examples and copy-paste fixes.
Perceivable
Information and UI components must be presentable in ways users can perceive. Nothing should be invisible to all their senses.
1.1.1ANon-text Content
All non-text content (images, icons, charts) must have a text alternative that serves the same purpose.
- ✗<img src="chart.png"> with no alt attribute
- ✗Decorative icon with aria-label that describes its appearance, not purpose
- ✗Form button with only an icon and no accessible name
<!-- Bad -->
<img src="revenue-chart.png">
<!-- Good -->
<img src="revenue-chart.png" alt="Revenue grew 40% from Q1 to Q4 2024">
<!-- Decorative (intentionally hidden from screen readers) -->
<img src="divider.png" alt="" role="presentation">1.3.1AInfo and Relationships
Information, structure, and relationships conveyed through presentation must be programmatically determinable.
- ✗Using bold text for headings instead of <h1>–<h6>
- ✗Required form fields indicated only by a red asterisk with no screen-reader label
- ✗Data tables without <th scope> attributes
<!-- Bad-->
<span style="font-weight: bold; font-size: 24px">Section Title</span>
<!-- Good -->
<h2>Section Title</h2>
<!-- Required field -->
<label for="email">
Email <span aria-hidden="true">*</span>
<span class="sr-only">(required)</span>
</label>1.4.3AAContrast (Minimum)
Text must have a contrast ratio of at least 4.5:1 (or 3:1 for large text ≥18pt or ≥14pt bold).
- ✗Light grey text (#999) on white backgrounds — ratio ≈ 2.85:1
- ✗Placeholder text with insufficient contrast
- ✗Disabled fields that still convey functional information
/* Bad: #aaa on white = 2.32:1 */
.hint-text { color: #aaaaaa; }
/* Good: #6b7280 on white = 4.6:1 */
.hint-text { color: #6b7280; }
/* Tip: Use Aulys's contrast checker or
WebAIM's contrast checker to validate */1.4.11AANon-text Contrast
UI components (form borders, focus indicators, icons) must have a 3:1 contrast ratio against adjacent colours.
- ✗Input borders that are the same colour as the page background
- ✗Focus rings that are light blue on white (insufficient contrast)
- ✗Chart lines that rely only on colour to distinguish them
/* Bad: #e5e7eb border on white is invisible */
input { border: 1px solid #e5e7eb; }
/* Good: #6b7280 on white = 4.6:1 */
input { border: 1px solid #6b7280; }
/* Focus indicator */
:focus-visible {
outline: 2px solid #4f46e5; /* 5.9:1 on white */
outline-offset: 2px;
}Operable
UI components and navigation must be operable. Users must be able to operate the interface with a keyboard alone.
2.1.1AKeyboard
All functionality must be available via keyboard without requiring specific timings for individual keystrokes.
- ✗Custom dropdown menus that only work with mouse hover
- ✗Drag-and-drop reordering with no keyboard alternative
- ✗Modal dialogs where focus is not trapped inside
// Trap focus inside a modal
function trapFocus(element) {
const focusableEls = element.querySelectorAll(
'a, button, input, [tabindex]:not([tabindex="-1"])'
);
const first = focusableEls[0];
const last = focusableEls[focusableEls.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
if (e.key === 'Escape') closeModal();
});
}2.4.3AFocus Order
If content can be navigated sequentially, the focus order must preserve meaning and operability.
- ✗Positive tabindex values that override the natural DOM order
- ✗Modals that open but leave focus on the trigger element
- ✗Single-page app navigation that does not move focus to the new view
<!-- Bad: tabindex=1 breaks natural order -->
<button tabindex="1">Submit</button>
<!-- Good: never use positive tabindex values -->
<button>Submit</button>
<!-- React: move focus on route change -->
useEffect(() => {
document.getElementById('main-heading')?.focus();
}, [pathname]);Understandable
Information and the operation of the UI must be understandable.
3.1.1ALanguage of Page
The default human language of each web page must be programmatically determinable.
- ✗Missing lang attribute on <html>
- ✗lang="en" on a page served in French
- ✗No lang attribute on embedded SVG with text content
<!-- Bad -->
<html>
<!-- Good -->
<html lang="en">
<!-- Multi-language content -->
<p>
The French word for hello is
<span lang="fr">bonjour</span>.
</p>3.3.1AError Identification
If an input error is detected, the item in error must be identified and described in text.
- ✗Validation errors indicated only by a red border
- ✗Error messages that disappear before the screen reader announces them
- ✗Generic "Something went wrong" messages that don't identify the field
<div role="alert" aria-live="polite">
<p>Please correct the following errors:</p>
<ul>
<li>Email address is required</li>
<li>Password must be at least 8 characters</li>
</ul>
</div>
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<span id="email-error" role="alert">
Email address is required
</span>Robust
Content must be robust enough to be interpreted by a wide variety of user agents, including assistive technologies.
4.1.2AName, Role, Value
For all UI components, the name, role, and value must be programmatically determinable and notification of changes available.
- ✗Custom checkboxes built with <div> but no role="checkbox"
- ✗Progress bars without aria-valuenow / aria-valuemax
- ✗Toggle buttons that change label but not aria-pressed
<!-- Bad: div acting as a button -->
<div onclick="submit()">Submit</div>
<!-- Good: semantic button -->
<button type="submit">Submit</button>
<!-- Custom checkbox -->
<div
role="checkbox"
aria-checked="false"
tabindex="0"
aria-labelledby="agree-label"
>
</div>
<span id="agree-label">I agree to the terms</span>
<!-- Toggle button -->
<button
aria-pressed="true"
onclick="this.setAttribute('aria-pressed',
this.getAttribute('aria-pressed') === 'true' ? 'false' : 'true')"
>
Dark mode
</button>Aulys checks all WCAG 2.2 A and AA criteria automatically.
Each scan result links directly to the relevant criterion listed above. For criteria requiring human judgment (like 1.4.4 Resize Text), Aulys flags the risk and provides guided checklists.
Run your first scan