wcag

WCAG 2.2 Accessibility

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "wcag" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-wcag

WCAG 2.2 Accessibility

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: wcag for comprehensive WCAG guidelines, success criteria, and techniques.

When NOT to Use This Skill

  • Automated testing setup - Use the axe-core skill for integrating axe testing tools

  • Component library accessibility - Use framework-specific skills (e.g., React, Vue) for accessible component patterns

  • Design systems - Use UI library skills for pre-built accessible components

  • ARIA implementation only - This skill covers broader WCAG compliance, not just ARIA

Official References

Resource URL

WCAG 2.2 Spec https://www.w3.org/TR/WCAG22/

Quick Reference https://www.w3.org/WAI/WCAG22/quickref/

Understanding WCAG https://www.w3.org/WAI/WCAG22/Understanding/

axe-core https://github.com/dequelabs/axe-core

WAI-ARIA https://www.w3.org/TR/wai-aria-1.2/

Conformance Levels

Level Description Legal Requirement

A Minimum accessibility Rarely sufficient

AA Standard accessibility Most regulations (ADA, EN 301 549)

AAA Enhanced accessibility Specialized contexts

WCAG 2.2 Success Criteria Count

Level New in 2.2 Total

A 0 30

AA 6 24

AAA 3 31

POUR Principles

  1. Perceivable

Guideline Key Criteria Level

1.1 Text Alternatives All non-text content has text alternative A

1.2 Time-based Media Captions, audio descriptions A-AAA

1.3 Adaptable Content structure, meaningful sequence A

1.4 Distinguishable Color contrast, resize text, spacing A-AAA

// Text alternatives <img src="chart.png" alt="Q3 sales increased 25% compared to Q2" />

// Decorative images <img src="divider.png" alt="" role="presentation" />

// Color contrast (4.5:1 for normal text, 3:1 for large text) // Use tools: WebAIM Contrast Checker, axe DevTools

  1. Operable

Guideline Key Criteria Level

2.1 Keyboard All functionality via keyboard A

2.2 Enough Time Adjustable time limits A-AAA

2.4 Navigable Skip links, focus order, focus visible A-AAA

2.5 Input Modalities Target size, motion alternatives A-AAA

// Skip link <a href="#main-content" className="skip-link"> Skip to main content </a>

// Focus visible (2.4.7) button:focus { outline: 3px solid #005fcc; outline-offset: 2px; }

// Target size minimum (2.5.8) - 24x24 CSS pixels .button { min-width: 44px; min-height: 44px; padding: 12px 16px; }

  1. Understandable

Guideline Key Criteria Level

3.1 Readable Language of page, unusual words A-AAA

3.2 Predictable Consistent navigation, identification A-AA

3.3 Input Assistance Error identification, labels A-AAA

// Language of page (3.1.1) <html lang="en">

// Error identification (3.3.1) <div role="alert" aria-live="assertive"> Email is required and must be valid </div>

// Labels (3.3.2) <label htmlFor="email">Email Address</label> <input id="email" type="email" aria-describedby="email-hint" /> <span id="email-hint">We'll never share your email</span>

  1. Robust

Guideline Key Criteria Level

4.1 Compatible Valid HTML, name/role/value A

// Name, Role, Value (4.1.2) <button aria-pressed="true" aria-label="Favorite this item"> ★ </button>

// Custom controls <div role="slider" aria-valuemin={0} aria-valuemax={100} aria-valuenow={50} aria-label="Volume" tabIndex={0} />

New in WCAG 2.2

Level AA (Required)

Criterion Description

2.4.11 Focus Not Obscured (Minimum) Focused element at least partially visible

2.4.13 Focus Appearance Focus indicator meets size/contrast requirements

2.5.7 Dragging Movements Single pointer alternative to drag

2.5.8 Target Size (Minimum) 24x24 CSS pixels minimum

3.2.6 Consistent Help Help in consistent location

3.3.7 Redundant Entry Don't require re-entering info

Level AAA

Criterion Description

2.4.12 Focus Not Obscured (Enhanced) Focused element fully visible

3.3.8 Accessible Authentication (Minimum) No cognitive function test

3.3.9 Accessible Authentication (Enhanced) No object/content recognition

Common Patterns

Modal Dialog

function Modal({ isOpen, onClose, title, children }) { const modalRef = useRef<HTMLDivElement>(null);

useEffect(() => { if (isOpen) { // Trap focus inside modal modalRef.current?.focus();

  // Prevent body scroll
  document.body.style.overflow = 'hidden';
}
return () => {
  document.body.style.overflow = '';
};

}, [isOpen]);

if (!isOpen) return null;

return ( <div role="dialog" aria-modal="true" aria-labelledby="modal-title" ref={modalRef} tabIndex={-1} onKeyDown={(e) => e.key === 'Escape' && onClose()} > <h2 id="modal-title">{title}</h2> {children} <button onClick={onClose}>Close</button> </div> ); }

Dropdown Menu

function Dropdown({ label, items }) { const [isOpen, setIsOpen] = useState(false); const [activeIndex, setActiveIndex] = useState(-1);

const handleKeyDown = (e: KeyboardEvent) => { switch (e.key) { case 'ArrowDown': e.preventDefault(); setActiveIndex(i => Math.min(i + 1, items.length - 1)); break; case 'ArrowUp': e.preventDefault(); setActiveIndex(i => Math.max(i - 1, 0)); break; case 'Escape': setIsOpen(false); break; case 'Enter': case ' ': if (activeIndex >= 0) items[activeIndex].onClick(); break; } };

return ( <div onKeyDown={handleKeyDown}> <button aria-haspopup="menu" aria-expanded={isOpen} onClick={() => setIsOpen(!isOpen)} > {label} </button> {isOpen && ( <ul role="menu"> {items.map((item, i) => ( <li key={item.id} role="menuitem" tabIndex={activeIndex === i ? 0 : -1} aria-current={activeIndex === i} > {item.label} </li> ))} </ul> )} </div> ); }

Form with Validation

function Form() { const [errors, setErrors] = useState<Record<string, string>>({});

return ( <form aria-describedby="form-errors"> {Object.keys(errors).length > 0 && ( <div id="form-errors" role="alert" aria-live="polite"> <h2>Please fix the following errors:</h2> <ul> {Object.entries(errors).map(([field, msg]) => ( <li key={field}> <a href={#${field}}>{msg}</a> </li> ))} </ul> </div> )}

  &#x3C;div>
    &#x3C;label htmlFor="email">
      Email &#x3C;span aria-hidden="true">*&#x3C;/span>
      &#x3C;span className="sr-only">(required)&#x3C;/span>
    &#x3C;/label>
    &#x3C;input
      id="email"
      type="email"
      aria-required="true"
      aria-invalid={!!errors.email}
      aria-describedby={errors.email ? 'email-error' : undefined}
    />
    {errors.email &#x26;&#x26; (
      &#x3C;span id="email-error" role="alert">{errors.email}&#x3C;/span>
    )}
  &#x3C;/div>
&#x3C;/form>

); }

Testing

Automated Testing with axe-core

// Playwright + axe import { test, expect } from '@playwright/test'; import AxeBuilder from '@axe-core/playwright';

test('homepage has no accessibility violations', async ({ page }) => { await page.goto('/');

const results = await new AxeBuilder({ page }) .withTags(['wcag2a', 'wcag2aa', 'wcag22aa']) .analyze();

expect(results.violations).toEqual([]); });

// Vitest + axe import { axe, toHaveNoViolations } from 'jest-axe'; import { render } from '@testing-library/react';

expect.extend(toHaveNoViolations);

test('Button is accessible', async () => { const { container } = render(<Button>Click me</Button>); const results = await axe(container); expect(results).toHaveNoViolations(); });

Manual Testing Checklist

Keyboard Navigation

  • Tab through all interactive elements
  • Shift+Tab navigates backwards
  • Enter/Space activates buttons and links
  • Arrow keys work in menus, tabs, sliders
  • Escape closes modals and dropdowns
  • No keyboard traps

Screen Reader

  • All images have alt text
  • Form fields have labels
  • Headings are hierarchical (h1 > h2 > h3)
  • Links are descriptive (not "click here")
  • Dynamic content announced (aria-live)

Visual

  • Color contrast meets 4.5:1 (normal text)
  • Color contrast meets 3:1 (large text, UI components)
  • Focus indicators visible
  • Content readable at 200% zoom
  • No horizontal scrolling at 320px width

Tools

Tool Purpose

axe DevTools Browser extension for auditing

WAVE Visual accessibility evaluation

Lighthouse Performance + accessibility audit

NVDA/VoiceOver Screen reader testing

Color Contrast Analyzer Contrast checking

CSS Utilities

/* Visually hidden but accessible to screen readers */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* Focus visible only for keyboard users */ :focus:not(:focus-visible) { outline: none; }

:focus-visible { outline: 3px solid #005fcc; outline-offset: 2px; }

/* 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; } }

/* High contrast mode support */ @media (forced-colors: active) { .button { border: 2px solid currentColor; } }

Checklist

Design Phase

  • Color contrast meets requirements

  • Touch targets 44x44 minimum (recommended)

  • Focus states designed

  • Error states include text, not just color

Development

  • Semantic HTML used

  • ARIA only when HTML insufficient

  • Keyboard navigation works

  • Focus management for SPAs

  • Form errors accessible

Testing

  • axe-core in CI pipeline

  • Manual screen reader testing

  • Keyboard-only navigation tested

  • Zoom to 200% tested

Anti-Patterns

Anti-Pattern Why It's Wrong Correct Approach

Using aria-label on <div> without role

ARIA on non-semantic elements without roles is ignored Use semantic HTML first: <button> instead of <div role="button">

<div onclick=""> for buttons Not keyboard accessible by default Use <button> with proper event handlers

Color-only indicators Fails for colorblind users Add icons, text, or patterns alongside color

placeholder as label replacement Disappears on input, low contrast Always use <label> with for attribute

tabindex

0 Disrupts natural tab order Use tabindex="0" or rely on DOM order

alt="" on informative images Screen readers skip important content Provide descriptive alt text

Missing focus indicators Users can't see where they are Always show :focus-visible styles

Auto-playing media Disorienting for screen reader users Require user action to start media

Quick Troubleshooting

Issue Diagnosis Solution

Screen reader not announcing button Missing accessible name Add aria-label or visible text inside button

Keyboard trap in modal Focus not properly managed Implement focus trap with first/last element logic

Form errors not announced No role="alert" or aria-live

Add aria-live="assertive" to error container

Low contrast warning Text doesn't meet 4.5:1 ratio Use contrast checker, adjust colors or increase font size

Tab order feels wrong DOM order doesn't match visual order Reorder DOM to match visual layout, avoid tabindex

0

Custom dropdown not accessible Missing ARIA roles and keyboard handling Use role="combobox" , aria-expanded , arrow key navigation

Dynamic content not announced Changes happen silently Use aria-live="polite" or role="status"

Image button has no label Only icon, no text alternative Add aria-label with descriptive text

Related Skills

  • React

  • Testing Library

  • Playwright

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review