
Accessibility
- 39.6k installs
- 2.5k repo stars
- Updated June 14, 2026
- addyosmani/web-quality-skills
accessibility is an agent skill for WCAG 2.2 web accessibility audits covering alt text, keyboard access, contrast, and assistive technology compatibility.
About
The accessibility skill from web-quality-skills provides WCAG 2.2 aligned guidance for auditing and improving web accessibility. It organizes recommendations around POUR: perceivable, operable, understandable, and robust content, targeting Level A minimum, AA standard compliance, and AAA enhancements where feasible. Perceivable rules cover alt text, icon button names, color contrast ratios, non-color error cues, and media captions. Operable guidance enforces keyboard access, visible focus, skip links, 24px minimum targets, dragging alternatives, and session timing warnings. Understandable sections address predictable navigation, input labels, and error identification. Robust patterns ensure semantic HTML and ARIA used correctly with assistive technologies. The skill includes concrete HTML, CSS, and JavaScript examples marked incorrect versus correct, plus references to modal focus traps, skip links, and dragging movement patterns. Invoke it for a11y audits, screen reader support, keyboard navigation, and WCAG compliance reviews.
- Structured around WCAG 2.2 POUR principles with A, AA, and AAA conformance targets.
- Documents alt text, icon labels, contrast tables, and non-color error presentation patterns.
- Keyboard accessibility rules with native element preference and focus-visible styling examples.
- Covers WCAG 2.2 additions: focus not obscured, 24px targets, and dragging movement alternatives.
- Links reference patterns for skip links, modal focus traps, and accessible form errors.
Accessibility by the numbers
- 39,635 all-time installs (skills.sh)
- +2,016 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #29 of 1,896 Design & UI/UX skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
accessibility capabilities & compatibility
- Capabilities
- wcag pour principle routing · alt text and media alternative patterns · keyboard and focus management guidance · color contrast and error presentation fixes · wcag 2.2 target size and dragging alternatives
- Use cases
- ui design · frontend · testing
What accessibility says it does
Goal: make content usable by everyone, including people with disabilities.
All functionality must be keyboard accessible.
npx skills add https://github.com/addyosmani/web-quality-skills --skill accessibilityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 39.6k |
|---|---|
| repo stars | ★ 2.5k |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 14, 2026 |
| Repository | addyosmani/web-quality-skills ↗ |
How do I find and fix accessibility issues so my web UI meets WCAG 2.2 AA expectations for keyboard, screen reader, and contrast users?
Audit and fix web accessibility against WCAG 2.2 POUR principles for perceivable, operable, understandable, robust UI.
Who is it for?
Frontend developers and reviewers improving a11y, screen reader support, keyboard navigation, or WCAG compliance.
Skip if: Skip for non-web surfaces or tasks unrelated to inclusive UI markup and interaction design.
When should I use this skill?
Improve accessibility, a11y audit, WCAG compliance, screen reader support, keyboard navigation, or make accessible.
What you get
Concrete HTML, CSS, and JS fixes aligned to WCAG POUR principles with incorrect versus correct examples.
- Accessibility issue findings mapped to WCAG criteria
- Corrected markup and style patterns
By the numbers
- Version 1.1 MIT-licensed skill from web-quality-skills author.
- Contrast table lists AA 4.5:1 normal text and 3:1 large text thresholds.
- Documents WCAG 2.2 criteria including 2.4.11 focus not obscured and 2.5.8 target size.
Files
Accessibility (a11y)
Comprehensive accessibility guidelines based on WCAG 2.2 and Lighthouse accessibility audits. Goal: make content usable by everyone, including people with disabilities.
WCAG Principles: POUR
| Principle | Description |
|---|---|
| Perceivable | Content can be perceived through different senses |
| Operable | Interface can be operated by all users |
| Understandable | Content and interface are understandable |
| Robust | Content works with assistive technologies |
Conformance levels
| Level | Requirement | Target |
|---|---|---|
| A | Minimum accessibility | Must pass |
| AA | Standard compliance | Should pass (legal requirement in many jurisdictions) |
| AAA | Enhanced accessibility | Nice to have |
---
Perceivable
Text alternatives (1.1)
Images require alt text:
<!-- ❌ Missing alt -->
<img src="chart.png">
<!-- ✅ Descriptive alt -->
<img src="chart.png" alt="Bar chart showing 40% increase in Q3 sales">
<!-- ✅ Decorative image (empty alt) -->
<img src="decorative-border.png" alt="" role="presentation">
<!-- ✅ Complex image with longer description -->
<figure>
<img src="infographic.png" alt="2024 market trends infographic"
aria-describedby="infographic-desc">
<figcaption id="infographic-desc">
<!-- Detailed description -->
</figcaption>
</figure>Icon buttons need accessible names:
<!-- ❌ No accessible name -->
<button><svg><!-- menu icon --></svg></button>
<!-- ✅ Using aria-label -->
<button aria-label="Open menu">
<svg aria-hidden="true"><!-- menu icon --></svg>
</button>
<!-- ✅ Using visually hidden text -->
<button>
<svg aria-hidden="true"><!-- menu icon --></svg>
<span class="visually-hidden">Open menu</span>
</button>Visually hidden class:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}Color contrast (1.4.3, 1.4.6)
| Text Size | AA minimum | AAA enhanced |
|---|---|---|
| Normal text (< 18px / < 14px bold) | 4.5:1 | 7:1 |
| Large text (≥ 18px / ≥ 14px bold) | 3:1 | 4.5:1 |
| UI components & graphics | 3:1 | 3:1 |
/* ❌ Low contrast (2.5:1) */
.low-contrast {
color: #999;
background: #fff;
}
/* ✅ Sufficient contrast (7:1) */
.high-contrast {
color: #333;
background: #fff;
}
/* ✅ Focus states need contrast too (3:1 against background, WCAG 1.4.11) */
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}Don't rely on color alone:
<!-- ❌ Only color indicates error -->
<input class="error-border">
<style>.error-border { border-color: red; }</style>
<!-- ✅ Color + icon + text -->
<div class="field-error">
<input aria-invalid="true" aria-describedby="email-error">
<span id="email-error" class="error-message">
<svg aria-hidden="true"><!-- error icon --></svg>
Please enter a valid email address
</span>
</div>Media alternatives (1.2)
<!-- Video with captions -->
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English" default>
<track kind="descriptions" src="descriptions.vtt" srclang="en" label="Descriptions">
</video>
<!-- Audio with transcript -->
<audio controls>
<source src="podcast.mp3" type="audio/mp3">
</audio>
<details>
<summary>Transcript</summary>
<p>Full transcript text...</p>
</details>---
Operable
Keyboard accessible (2.1)
All functionality must be keyboard accessible. Prefer native interactive elements — <button>, <a href>, and form controls handle Enter/Space activation, focus, and assistive-tech semantics for free. Only add manual keyboard handling when you cannot use a native element.
<!-- ❌ Non-interactive element with click only: not focusable, no keyboard activation -->
<div class="card" onclick="handleAction()">Open</div>
<!-- ✅ Best: use a native button -->
<button type="button" onclick="handleAction()">Open</button>// ✅ When you MUST use a non-interactive element (e.g. div with role="button"),
// make it focusable AND handle keyboard activation. Do NOT add this to a native
// <button> — Enter/Space already fire click, so you'd double-trigger.
element.setAttribute('role', 'button');
element.setAttribute('tabindex', '0');
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAction();
}
});No keyboard traps. Users must be able to Tab into and out of every component. Use the modal focus trap pattern for dialogs—the native <dialog> element handles this automatically.
Focus visible (2.4.7)
/* ❌ Never remove focus outlines */
*:focus { outline: none; }
/* ✅ Use :focus-visible for keyboard-only focus */
:focus {
outline: none;
}
:focus-visible {
outline: 2px solid currentColor; /* inherits text color → already contrast-checked */
outline-offset: 2px;
}
/* ✅ Or pick a brand color and verify ≥3:1 contrast against every background it lands on */
button:focus-visible {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
}Focus not obscured (2.4.11) — new in 2.2
When an element receives keyboard focus, it must not be entirely hidden by other author-created content such as sticky headers, footers, or overlapping panels. At Level AAA (2.4.12), no part of the focused element may be hidden.
/* ✅ Account for sticky headers when scrolling to focused elements */
:target {
scroll-margin-top: 80px;
}
/* ✅ Ensure focused items clear fixed/sticky bars */
:focus {
scroll-margin-top: 80px;
scroll-margin-bottom: 60px;
}Skip links (2.4.1)
Provide a skip link so keyboard users can bypass repetitive navigation. See the skip link pattern for full markup and styles.
Target size (2.5.8) — new in 2.2
Interactive targets must be at least 24 × 24 CSS pixels (AA). Exceptions: inline text links, elements where the browser controls the size, and targets where a 24px circle centered on the bounding box does not overlap another target.
/* ✅ Minimum target size */
button,
[role="button"],
input[type="checkbox"] + label,
input[type="radio"] + label {
min-width: 24px;
min-height: 24px;
}
/* ✅ Comfortable target size (recommended 44×44) */
.touch-target {
min-width: 44px;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
}Dragging movements (2.5.7) — new in 2.2
Any action that requires dragging must have a single-pointer alternative (e.g., buttons, inputs). See the dragging movements pattern for a sortable-list example.
Timing (2.2)
// Allow users to extend time limits
function showSessionWarning() {
const modal = createModal({
title: 'Session Expiring',
content: 'Your session will expire in 2 minutes.',
actions: [
{ label: 'Extend session', action: extendSession },
{ label: 'Log out', action: logout }
],
timeout: 120000
});
}Motion (2.3)
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}---
Understandable
Page language (3.1.1)
<!-- ❌ No language specified -->
<html>
<!-- ✅ Language specified -->
<html lang="en">
<!-- ✅ Language changes within page -->
<p>The French word for hello is <span lang="fr">bonjour</span>.</p>Consistent navigation (3.2.3)
<!-- Navigation should be consistent across pages -->
<nav aria-label="Main">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>Consistent help (3.2.6) — new in 2.2
If a help mechanism (contact info, chat widget, FAQ link, self-help option) is repeated across multiple pages, it must appear in the same relative order each time. Users who rely on consistent placement shouldn't have to hunt for help on every page.
Form labels (3.3.2)
Every input needs a programmatically associated label. See the form labels pattern for explicit, implicit, and instructional examples.
Error handling (3.3.1, 3.3.3)
Announce errors to screen readers with role="alert" or aria-live, set aria-invalid="true" on invalid fields, and focus the first error on submit. See the error handling pattern for full markup and JS.
Redundant entry (3.3.7) — new in 2.2
Don't force users to re-enter information they already provided in the same session. Auto-populate from earlier steps, or let users select from previously entered values. Exceptions: security re-confirmation and content that has expired.
<!-- ✅ Auto-fill shipping address from billing -->
<fieldset>
<legend>Shipping address</legend>
<label>
<input type="checkbox" id="same-as-billing" checked>
Same as billing address
</label>
<!-- Fields auto-populated when checked -->
</fieldset>Accessible authentication (3.3.8) — new in 2.2
Login flows must not rely on cognitive function tests (e.g., remembering a password, solving a puzzle) unless at least one of:
- A copy-paste or autofill mechanism is available
- An alternative method exists (e.g., passkey, SSO, email link)
- The test uses object recognition or personal content (AA only; AAA removes this exception)
<!-- ✅ Allow paste in password fields -->
<input type="password" id="password" autocomplete="current-password">
<!-- ✅ Offer passwordless alternatives -->
<button type="button">Sign in with passkey</button>
<button type="button">Email me a login link</button>---
Robust
ARIA usage (4.1.2)
Prefer native elements:
<!-- ❌ ARIA role on div -->
<div role="button" tabindex="0">Click me</div>
<!-- ✅ Native button -->
<button>Click me</button>
<!-- ❌ ARIA checkbox -->
<div role="checkbox" aria-checked="false">Option</div>
<!-- ✅ Native checkbox -->
<label><input type="checkbox"> Option</label>When ARIA is needed, use the correct roles and states. See the ARIA tabs pattern for a complete tablist example.
Live regions (4.1.3)
Use aria-live regions to announce dynamic content changes without moving focus. See the live regions pattern for markup and a showNotification() helper.
---
Testing checklist
Automated testing
# Lighthouse accessibility audit
npx lighthouse https://example.com --only-categories=accessibility
# axe-core
npm install @axe-core/cli -g
axe https://example.comManual testing
- [ ] Keyboard navigation: Tab through entire page, use Enter/Space to activate
- [ ] Screen reader: Test with VoiceOver (Mac), NVDA (Windows), or TalkBack (Android)
- [ ] Zoom: Content usable at 200% zoom
- [ ] High contrast: Test with Windows High Contrast Mode
- [ ] Reduced motion: Test with
prefers-reduced-motion: reduce - [ ] Focus order: Logical and follows visual order
- [ ] Target size: Interactive elements meet 24×24px minimum
See the screen reader commands reference for VoiceOver and NVDA shortcuts.
---
Common issues by impact
Critical (fix immediately)
1. Missing form labels 2. Missing image alt text 3. Insufficient color contrast 4. Keyboard traps 5. No focus indicators
Serious (fix before launch)
1. Missing page language 2. Missing heading structure 3. Non-descriptive link text 4. Auto-playing media 5. Missing skip links
Moderate (fix soon)
1. Missing ARIA labels on icons 2. Inconsistent navigation 3. Missing error identification 4. Timing without controls 5. Missing landmark regions
References
- WCAG 2.2 Quick Reference
- WAI-ARIA Authoring Practices
- Deque axe Rules
- Web Quality Audit
- WCAG criteria reference
- Accessibility code patterns
Accessibility Code Patterns
Practical, copy-paste-ready patterns for common accessibility requirements. Each pattern is self-contained and linked from the main SKILL.md.
---
Modal focus trap
Trap keyboard focus inside a modal dialog so Tab/Shift+Tab cycle through its focusable elements and Escape closes it.
function openModal(modal) {
const focusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
modal.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
if (e.key === 'Escape') {
closeModal();
}
});
firstElement.focus();
}The native <dialog> element handles focus trapping automatically—prefer it when browser support allows.
---
Skip link
Allows keyboard users to bypass repetitive navigation and jump straight to main content.
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header><!-- navigation --></header>
<main id="main-content" tabindex="-1">
<!-- main content -->
</main>
</body>.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}---
Error handling
Announce errors to screen readers and focus the first invalid field on submit.
<form novalidate>
<div class="field" aria-live="polite">
<label for="email">Email</label>
<input type="email" id="email"
aria-invalid="true"
aria-describedby="email-error">
<p id="email-error" class="error" role="alert">
Please enter a valid email address (e.g., name@example.com)
</p>
</div>
</form>form.addEventListener('submit', (e) => {
const firstError = form.querySelector('[aria-invalid="true"]');
if (firstError) {
e.preventDefault();
firstError.focus();
const errorSummary = document.getElementById('error-summary');
errorSummary.textContent =
`${errors.length} errors found. Please fix them and try again.`;
errorSummary.focus();
}
});---
Form labels
Every input needs an associated label—either explicit (for/id) or implicit (wrapping <label>).
<!-- ❌ No label association -->
<input type="email" placeholder="Email">
<!-- ✅ Explicit label -->
<label for="email">Email address</label>
<input type="email" id="email" name="email"
autocomplete="email" required>
<!-- ✅ Implicit label -->
<label>
Email address
<input type="email" name="email" autocomplete="email" required>
</label>
<!-- ✅ With instructions -->
<label for="password">Password</label>
<input type="password" id="password"
aria-describedby="password-requirements">
<p id="password-requirements">
Must be at least 8 characters with one number.
</p>---
Dragging movements
Any action triggered by dragging must offer a single-pointer alternative (WCAG 2.5.7).
<!-- ❌ Drag-only reorder -->
<ul class="sortable-list" draggable="true">
<li>Item 1</li>
<li>Item 2</li>
</ul>
<!-- ✅ Drag + button alternatives -->
<ul class="sortable-list">
<li>
<span>Item 1</span>
<button aria-label="Move Item 1 up">↑</button>
<button aria-label="Move Item 1 down">↓</button>
</li>
<li>
<span>Item 2</span>
<button aria-label="Move Item 2 up">↑</button>
<button aria-label="Move Item 2 down">↓</button>
</li>
</ul>Also applies to sliders, map panning, colour pickers, and similar drag-based widgets—always provide an equivalent click/tap or keyboard path.
---
ARIA tabs
Tabs require role="tablist", role="tab", and role="tabpanel" with proper aria-selected, aria-controls, and keyboard support.
<div role="tablist" aria-label="Product information">
<button role="tab" id="tab-1" aria-selected="true"
aria-controls="panel-1">Description</button>
<button role="tab" id="tab-2" aria-selected="false"
aria-controls="panel-2" tabindex="-1">Reviews</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<!-- Panel content -->
</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
<!-- Panel content -->
</div>Arrow keys should move focus between tabs; the active tab receives tabindex="0" while inactive tabs use tabindex="-1".
---
Live regions and notifications
Use aria-live to announce dynamic content changes to screen readers without moving focus.
<!-- Status updates (polite — waits for pause in speech) -->
<div aria-live="polite" aria-atomic="true" class="status">
<!-- Content updates announced to screen readers -->
</div>
<!-- Urgent alerts (assertive — interrupts) -->
<div role="alert" aria-live="assertive">
<!-- Interrupts current announcement -->
</div>function showNotification(message, type = 'polite') {
const container = document.getElementById(`${type}-announcer`);
container.textContent = '';
requestAnimationFrame(() => {
container.textContent = message;
});
}Clear the container before writing to ensure the same message triggers a new announcement.
---
Screen reader commands
Quick reference for the most common screen reader shortcuts.
| Action | VoiceOver (Mac) | NVDA (Windows) |
|---|---|---|
| Start/Stop | ⌘ + F5 | Ctrl + Alt + N |
| Next item | VO + → | ↓ |
| Previous item | VO + ← | ↑ |
| Activate | VO + Space | Enter |
| Headings list | VO + U, then arrows | H / Shift + H |
| Links list | VO + U | K / Shift + K |
WCAG 2.2 Quick Reference
Success criteria by level
Level A (minimum)
| Criterion | Description |
|---|---|
| 1.1.1 Non-text Content | All images, icons have text alternatives |
| 1.2.1 Audio-only/Video-only | Provide transcript or audio description |
| 1.2.2 Captions | Video with audio has captions |
| 1.2.3 Audio Description | Video has audio description |
| 1.3.1 Info and Relationships | Information conveyed through presentation is available programmatically |
| 1.3.2 Meaningful Sequence | Reading order is logical |
| 1.3.3 Sensory Characteristics | Instructions don't rely solely on shape, color, size, location, orientation, or sound |
| 1.4.1 Use of Color | Color is not the only visual means of conveying information |
| 1.4.2 Audio Control | Audio playing automatically can be paused/stopped |
| 2.1.1 Keyboard | All functionality available via keyboard |
| 2.1.2 No Keyboard Trap | Keyboard focus can be moved away from any component |
| 2.1.4 Character Key Shortcuts | Single-key shortcuts can be turned off or remapped |
| 2.2.1 Timing Adjustable | Time limits can be extended |
| 2.2.2 Pause, Stop, Hide | Moving/blinking content can be paused |
| 2.3.1 Three Flashes | Nothing flashes more than 3 times per second |
| 2.4.1 Bypass Blocks | Skip link or landmark navigation available |
| 2.4.2 Page Titled | Pages have descriptive titles |
| 2.4.3 Focus Order | Focus order preserves meaning |
| 2.4.4 Link Purpose | Link purpose clear from link text or context |
| 2.5.1 Pointer Gestures | Multi-point gestures have single-pointer alternatives |
| 2.5.2 Pointer Cancellation | Down-event doesn't trigger action (use up-event or click) |
| 2.5.3 Label in Name | Accessible name contains visible label text |
| 2.5.4 Motion Actuation | Motion-triggered functions have alternatives |
| 3.1.1 Language of Page | Default language specified in HTML |
| 3.2.1 On Focus | Focus doesn't trigger unexpected changes |
| 3.2.2 On Input | Input doesn't trigger unexpected changes |
| 3.2.6 Consistent Help | Help mechanisms appear in the same relative order across pages |
| 3.3.1 Error Identification | Input errors clearly described |
| 3.3.2 Labels or Instructions | Form inputs have labels or instructions |
| 3.3.7 Redundant Entry | Information previously entered is auto-populated or available to select |
| 4.1.2 Name, Role, Value | UI components have accessible names and correct roles |
Level AA (standard)
| Criterion | Description |
|---|---|
| 1.2.4 Captions (Live) | Live audio has captions |
| 1.2.5 Audio Description | Pre-recorded video has audio description |
| 1.3.4 Orientation | Content doesn't restrict orientation |
| 1.3.5 Identify Input Purpose | Input purpose can be programmatically determined |
| 1.4.3 Contrast (Minimum) | 4.5:1 for normal text, 3:1 for large text |
| 1.4.4 Resize Text | Text can be resized to 200% without loss of functionality |
| 1.4.5 Images of Text | Text used instead of images of text |
| 1.4.10 Reflow | Content reflows at 320px width without horizontal scroll |
| 1.4.11 Non-text Contrast | UI components have 3:1 contrast |
| 1.4.12 Text Spacing | Content adapts to text spacing changes |
| 1.4.13 Content on Hover/Focus | Additional content is dismissible, hoverable, persistent |
| 2.4.5 Multiple Ways | Multiple ways to find pages |
| 2.4.6 Headings and Labels | Headings and labels are descriptive |
| 2.4.7 Focus Visible | Focus indicator is visible |
| 2.4.11 Focus Not Obscured (Minimum) | Focused element is not entirely hidden by author-created content |
| 2.5.7 Dragging Movements | Dragging actions have single-pointer alternatives |
| 2.5.8 Target Size (Minimum) | Interactive targets are at least 24×24 CSS pixels (with exceptions) |
| 3.1.2 Language of Parts | Language changes are marked |
| 3.2.3 Consistent Navigation | Navigation is consistent across pages |
| 3.2.4 Consistent Identification | Same functionality uses same labels |
| 3.3.3 Error Suggestion | Error corrections suggested when known |
| 3.3.4 Error Prevention (Legal) | Actions can be reversed or confirmed |
| 3.3.8 Accessible Authentication (Minimum) | No cognitive function test for login unless an alternative or assistance is provided |
| 4.1.3 Status Messages | Status messages announced to screen readers |
Level AAA (enhanced)
| Criterion | Description |
|---|---|
| 1.4.6 Contrast (Enhanced) | 7:1 for normal text, 4.5:1 for large text |
| 1.4.8 Visual Presentation | Foreground/background colors can be selected |
| 1.4.9 Images of Text (No Exception) | No images of text |
| 2.1.3 Keyboard (No Exception) | All functionality keyboard accessible |
| 2.2.3 No Timing | No time limits |
| 2.2.4 Interruptions | Interruptions can be postponed |
| 2.2.5 Re-authenticating | Data preserved on re-authentication |
| 2.2.6 Timeouts | Users warned about data loss from inactivity |
| 2.3.2 Three Flashes | No content flashes more than 3 times |
| 2.3.3 Animation from Interactions | Motion animation can be disabled |
| 2.4.8 Location | User location within site is available |
| 2.4.9 Link Purpose (Link Only) | Link purpose clear from link text alone |
| 2.4.10 Section Headings | Sections have headings |
| 2.4.12 Focus Not Obscured (Enhanced) | No part of the focused element is hidden by author-created content |
| 2.4.13 Focus Appearance | Focus indicator has sufficient area, contrast, and is not obscured |
| 3.1.3 Unusual Words | Definitions available for unusual words |
| 3.1.4 Abbreviations | Abbreviations expanded |
| 3.1.5 Reading Level | Alternative content for complex text |
| 3.1.6 Pronunciation | Pronunciation available where needed |
| 3.2.5 Change on Request | Changes initiated only by user |
| 3.3.5 Help | Context-sensitive help available |
| 3.3.6 Error Prevention (All) | All form submissions can be reviewed |
| 3.3.9 Accessible Authentication (Enhanced) | No cognitive function test for login (no object or personal content recognition exceptions) |
Common ARIA patterns
Buttons
<button>Label</button>
<!-- or -->
<button aria-label="Close dialog">×</button>Links
<a href="/page">Descriptive link text</a>
<!-- External links -->
<a href="https://external.com" target="_blank" rel="noopener">
External site
<span class="visually-hidden">(opens in new tab)</span>
</a>Form fields
<label for="email">Email address</label>
<input type="email" id="email" aria-describedby="email-hint">
<p id="email-hint">We'll never share your email.</p>Error states
<label for="email">Email</label>
<input type="email" id="email" aria-invalid="true" aria-describedby="email-error">
<p id="email-error" role="alert">Please enter a valid email address.</p>Navigation
<nav aria-label="Main">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>Modals
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Action</h2>
<!-- content -->
</div>Live regions
<!-- Polite (waits for pause in speech) -->
<div aria-live="polite">Status update here</div>
<!-- Assertive (interrupts immediately) -->
<div aria-live="assertive" role="alert">Error message here</div>
<!-- Status (polite, implicit) -->
<div role="status">Loading complete</div>What changed from 2.1 to 2.2
| Change | Criterion | Level |
|---|---|---|
| Removed | 4.1.1 Parsing | A |
| Added | 2.4.11 Focus Not Obscured (Minimum) | AA |
| Added | 2.4.12 Focus Not Obscured (Enhanced) | AAA |
| Added | 2.4.13 Focus Appearance | AAA |
| Added | 2.5.7 Dragging Movements | AA |
| Added | 2.5.8 Target Size (Minimum) | AA |
| Added | 3.2.6 Consistent Help | A |
| Added | 3.3.7 Redundant Entry | A |
| Added | 3.3.8 Accessible Authentication (Minimum) | AA |
| Added | 3.3.9 Accessible Authentication (Enhanced) | AAA |
Testing tools
| Tool | Type | URL |
|---|---|---|
| axe DevTools | Browser extension | deque.com/axe |
| WAVE | Browser extension | wave.webaim.org |
| Lighthouse | Built into Chrome | DevTools → Lighthouse |
| NVDA | Screen reader (Windows) | nvaccess.org |
| VoiceOver | Screen reader (Mac) | Built into macOS |
| Colour Contrast Analyser | Desktop app | tpgi.com |
Sources
Related skills
Forks & variants (1)
Accessibility has 1 known copy in the catalog totaling 131 installs. They canonicalize to this original listing.
- thedesignproject - 131 installs
How it compares
WCAG 2.2 audit skill, not a general visual design or performance optimization guide.
FAQ
Who is accessibility for?
Web developers auditing or fixing UI against WCAG 2.2 for keyboard, contrast, and assistive technology compatibility.
When should I use accessibility?
During a11y audits, before release compliance checks, or when fixing focus, alt text, contrast, or keyboard trap issues.
Is accessibility safe to install?
Review the Security Audits panel on this page before installing in production.