WCAG 2.2 Accessibility Audit skill for Codex

Findings mapped to criteria, with fixes. Codex runs a real accessibility audit instead of quoting a Lighthouse score: it scopes representative templates and journeys, scans every UI state with axe-core, then does keyboard, screen reader, zoom, contrast and form passes covering the new 2.2 criteria. Findings carry a success criterion, evidence, affected users and a code fix, and the report states what was not tested.

  • Frontend
  • $wcag-accessibility-audit
  • 176 lines
  • 3 files
  • ~2,100 tokens
  • MIT
  • Updated
  • Editor's pick

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- wcag-accessibility-audit

Installs to ~/.agents/skills/wcag-accessibility-audit/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $wcag-accessibility-audit, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Audits web pages and UI components against WCAG 2.2 Level AA - automated axe-core scan, then manual keyboard, screen reader, zoom/reflow, contrast and forms passes - and produces a findings report mapped to success criteria with severity, evidence and code fixes. Use when a user asks for an accessibility audit, a WCAG or ADA/EAA compliance check, a VPAT/ACR input, a review of a page or component for screen reader and keyboard users, or wants to know which WCAG 2.2 criteria a UI fails.

Use it when

  • A page, flow or component library needs a WCAG 2.2 AA assessment
  • Preparing evidence for an accessibility conformance report or legal review
  • Users report they cannot complete a task with a keyboard or screen reader

Use something else when

How to check the result

Every finding cites a real success criterion with evidence and a fix, the coverage table accounts for all A and AA criteria, and fixed issues are re-tested with the method that found them.

The mistake it prevents

Calling an automated scan of the initial page load an audit; most failures only appear in keyboard, screen reader and error-state testing.

~/.agents/skills/wcag-accessibility-audit/SKILL.md

--- name: wcag-accessibility-audit description: Audits web pages and UI components against WCAG 2.2 Level AA - automated axe-core scan, then manual keyboard, screen reader, zoom/reflow, contrast… license: MIT ---

Done well means: every finding cites a success criterion, shows evidence (selector, screenshot or reproduction steps), states who is affected, and has a concrete fix; the report says what was tested, how, and what was not covered. Automated tools find roughly a third of issues; an audit without the manual passes is a scan, and must be labeled as one.

Legal compliance (ADA, Section 508, EN 301 549 / European Accessibility Act) is a legal judgment. Report against WCAG and say plainly that a legal or accessibility specialist should confirm conformance claims and any VPAT/ACR before publication.

Scope first

Agree on this before testing and write it at the top of the report:

  • Pages or components in scope (pick representative templates: home, list, detail, form, checkout, auth, error page), plus key user journeys.
  • Target: WCAG 2.2 Level A + AA (default). AAA only if asked.
  • Environments: browsers and screen readers (default: Chrome + NVDA or JAWS on Windows, Safari + VoiceOver on macOS/iOS, Chrome + TalkBack on Android when mobile is in scope).
  • Test accounts and data. Use the project’s seed or fixture credentials for local or staging environments; never sign in to production with real user data for an audit.

Workflow

- [ ] 1. Read AGENTS.md; find how to run the app locally and any existing a11y tests
- [ ] 2. Automated scan of each page and state (axe), save raw results
- [ ] 3. Keyboard pass
- [ ] 4. Screen reader pass on key journeys
- [ ] 5. Visual pass: zoom 200%, reflow at 320 CSS px, text spacing, contrast, motion
- [ ] 6. Forms and errors pass
- [ ] 7. Code review of semantics (landmarks, headings, names, ARIA usage)
- [ ] 8. De-duplicate, map to criteria, assign severity, write fixes
- [ ] 9. Report (template below); re-test fixes if asked

1. Automated scan

Use what the project has; otherwise a one-off Playwright script (packages need network):

// a11y-scan.spec.ts — npx playwright test a11y-scan.spec.ts
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";

for (const path of ["/", "/products", "/checkout"]) {
  test(`axe ${path}`, async ({ page }) => {
    await page.goto(path);
    const results = await new AxeBuilder({ page })
      .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
      .analyze();
    console.log(JSON.stringify(results.violations, null, 2));
    expect(results.violations).toEqual([]);
  });
}
  • Scan every state, not just page load: open menus, dialogs, validation errors, empty and loading states. Run axe again after each interaction.
  • Treat axe “incomplete” results as manual checks to do, not passes.
  • Lighthouse’s accessibility score is a subset of axe; never cite it as conformance.

2. Keyboard pass

Unplug the mouse mentally. For each page:

  • Tab through everything: every interactive element is reachable, in a logical order, with a clearly visible focus indicator (2.1.1, 2.4.3, 2.4.7).
  • Focused element is not hidden behind sticky headers, cookie banners or chat widgets (2.4.11 Focus Not Obscured, new in 2.2).
  • No traps: you can leave every widget, including embedded iframes and modals (2.1.2).
  • Composite widgets follow expected keys (arrows in menus/tabs/listboxes, Escape closes, Enter/Space activate); compare against $aria-patterns.
  • Modals move focus in, keep it in, and return it to the trigger on close.
  • A skip link or landmarks let users bypass repeated navigation (2.4.1).
  • Single-character shortcuts can be turned off or remapped (2.1.4).

3. Screen reader pass

Run the main journeys end to end. Check that:

  • Page <title> and the h1 identify the page; headings form an outline (1.3.1, 2.4.2, 2.4.6).
  • Landmarks exist (header, nav, main, footer) and are labeled when repeated.
  • Every control announces name, role, state and value (4.1.2); icon buttons are not “button”.
  • Images have meaningful alt, decorative images are silent (1.1.1).
  • Dynamic updates (toasts, cart count, search results, errors) are announced (4.1.3).
  • Reading order matches visual order (1.3.2).

4. Visual pass

  • Zoom to 200%: no loss of content or function (1.4.4). At 1280px wide and 400% zoom (320 CSS px) content reflows to one column with no horizontal scroll, except tables, maps and similar (1.4.10).
  • Text spacing override (line-height 1.5, paragraph 2x, letter 0.12em, word 0.16em) does not clip text (1.4.12).
  • Contrast: text 4.5:1, large text (24px, or 18.66px bold) 3:1, UI component boundaries, focus indicators and meaningful icons 3:1 (1.4.3, 1.4.11). Check hover, focus, disabled- looking-but-enabled and placeholder states. Color is never the only cue (1.4.1).
  • Pointer targets are at least 24x24 CSS px or have enough spacing (2.5.8, new in 2.2).
  • Content on hover/focus (tooltips) is dismissible, hoverable and persistent (1.4.13).
  • Motion: no flashing more than 3 times per second (2.3.1); auto-moving content over 5 s can be paused (2.2.2); orientation not locked (1.3.4).

5. Forms and errors

  • Every field has a programmatic label; placeholder is not a label (1.3.1, 3.3.2, 2.5.3 label in name matches visible text).
  • Required fields and formats are stated before submission; errors identify the field and describe the fix in text (3.3.1, 3.3.3); errors on legal or financial submissions can be reviewed or reversed (3.3.4).
  • Personal data fields have correct autocomplete tokens (1.3.5).
  • Information already entered in the same process is not asked for again (3.3.7, new in 2.2).
  • Login does not require a cognitive test such as transcribing or remembering; paste and password managers work (3.3.8, new in 2.2).
  • Help mechanisms (contact, chat) appear in the same relative place on each page (3.2.6).
  • Drag-only interactions have a single-pointer alternative (2.5.7, new in 2.2).

For the full A/AA criterion list with test methods, read references/wcag-22-aa-checklist.md when building the report’s coverage table or when a finding’s criterion is unclear.

Severity

SeverityMeaning
CriticalBlocks a task for a user group (keyboard trap, unlabeled submit, inaccessible checkout step)
SeriousTask possible but very hard or error-prone (missing error association, focus lost after dialog)
ModerateFriction or confusion (heading levels skipped, vague link text)
MinorPolish; low impact (redundant title attribute)

Group repeated issues from a shared component into one finding with all locations listed; fixing the component fixes them all.

Gotchas

  • Do not “fix” with ARIA what native HTML solves; role="button" on a div still needs keyboard handling. First rule of ARIA: use the native element.
  • aria-hidden="true" on a focusable element creates a ghost focus stop; check with Tab.
  • Overlay widgets that claim to fix accessibility automatically do not produce conformance; note them in the report if present.
  • outline: none with no replacement is the most common 2.4.7 failure; check :focus-visible.
  • An axe pass on the initial load says nothing about the error state of a form.
  • 4.1.1 Parsing is obsolete in WCAG 2.2; do not report it.

Report template

# Accessibility audit: <product>, <date>
**Standard:** WCAG 2.2 Level AA   **Method:** axe-core 4.x + manual (keyboard, NVDA 2025.x/Chrome, VoiceOver/Safari)
**Scope:** /, /search, /product/:id, /checkout (guest), sign-in   **Not tested:** PDFs, admin area
**Summary:** 4 critical, 7 serious, 9 moderate, 3 minor. Checkout cannot be completed by keyboard.

| # | Severity | WCAG SC | Location | Issue | Who is affected | Fix |
|---|---|---|---|---|---|---|
| 1 | Critical | 2.1.2 No Keyboard Trap | /checkout, address modal | Focus cannot leave the postcode lookup iframe | Keyboard, switch users | Close lookup on Escape; return focus to field |

### Finding 1 details
Steps to reproduce, evidence (selector, screenshot), code fix (diff), how to re-test.

**Coverage:** table of criteria: Pass / Fail / Not applicable / Not tested
**Note:** This is a technical assessment against WCAG, not legal advice on compliance.

Verification

  • Every finding has an SC number, evidence and a fix; no SC cited that does not exist.
  • Each fixed issue was re-tested with the same method that found it (axe re-run plus the manual step).
  • The coverage table accounts for every A and AA criterion.

What the zip contains

Codex reads bundled files only when a step points to them. Every one is shown here, so you can review it before installing.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "WCAG 2.2 Accessibility Audit"
  short_description: "Findings mapped to criteria, with fixes"
  default_prompt: "$wcag-accessibility-audit Audit our checkout flow against WCAG 2.2 AA and give me a prioritized report"
references/wcag-22-aa-checklist.md91 lines, 7.5 KB
# WCAG 2.2 Level A and AA: test checklist

55 success criteria (31 A, 24 AA). 4.1.1 Parsing was removed in 2.2 and is not listed.
"Auto" means axe-core can flag some failures; every row still needs the manual check.
Paraphrased for testing; cite the official criterion name in reports.

## 1 Perceivable

| SC | Level | Name | How to test |
|---|---|---|---|
| 1.1.1 | A | Non-text Content | Auto + manual: informative images have alt that conveys purpose; decorative ones `alt=""`; icon buttons, charts, CAPTCHAs have text alternatives |
| 1.2.1 | A | Audio-only and Video-only (Prerecorded) | Transcript for audio-only; transcript or audio track for silent video |
| 1.2.2 | A | Captions (Prerecorded) | Captions present, synchronized, include speakers and meaningful sounds |
| 1.2.3 | A | Audio Description or Media Alternative (Prerecorded) | Visual-only information in video is described or a full text alternative exists |
| 1.2.4 | AA | Captions (Live) | Live streams have real-time captions |
| 1.2.5 | AA | Audio Description (Prerecorded) | Audio description track for visual information not in the dialogue |
| 1.3.1 | A | Info and Relationships | Auto + manual: headings, lists, tables (`th`, scope), labels, fieldset/legend and landmarks expressed in markup, not only styling |
| 1.3.2 | A | Meaningful Sequence | DOM/reading order matches visual order; check CSS order, grid placement, absolute positioning |
| 1.3.3 | A | Sensory Characteristics | Instructions do not rely only on shape, position, color or sound ("click the round button on the right") |
| 1.3.4 | AA | Orientation | Works in portrait and landscape; no orientation lock unless essential |
| 1.3.5 | AA | Identify Input Purpose | Auto: user-data fields have correct `autocomplete` tokens (`email`, `given-name`, `street-address`, `one-time-code`) |
| 1.4.1 | A | Use of Color | Links in body text, errors, required fields, chart series are distinguishable without color |
| 1.4.2 | A | Audio Control | Audio that autoplays more than 3 s can be paused or muted independently |
| 1.4.3 | AA | Contrast (Minimum) | Auto + manual: 4.5:1 text, 3:1 large text; check states, text over images and gradients |
| 1.4.4 | AA | Resize Text | Browser zoom 200%: nothing clipped, overlapping or lost |
| 1.4.5 | AA | Images of Text | Real text used instead of text baked into images, except logos |
| 1.4.10 | AA | Reflow | 320 CSS px width (1280 px at 400%): single column, no two-dimensional scrolling except for data tables, maps, diagrams |
| 1.4.11 | AA | Non-text Contrast | 3:1 for input borders, focus rings, toggles, checkbox outlines, meaningful icons and chart elements against adjacent colors |
| 1.4.12 | AA | Text Spacing | Apply spacing bookmarklet (1.5 line, 2x paragraph, 0.12em letter, 0.16em word): no clipped or overlapping text |
| 1.4.13 | AA | Content on Hover or Focus | Tooltips/popovers: dismiss with Escape without moving pointer, pointer can move onto them, stay until dismissed |

## 2 Operable

| SC | Level | Name | How to test |
|---|---|---|---|
| 2.1.1 | A | Keyboard | Every function works with keyboard alone; no hover-only or drag-only controls |
| 2.1.2 | A | No Keyboard Trap | Focus can always move away (including iframes, editors, modals by Escape or close button) |
| 2.1.4 | A | Character Key Shortcuts | Single-key shortcuts can be turned off, remapped, or only work when the component has focus |
| 2.2.1 | A | Timing Adjustable | Session timeouts warn and allow extension (at least 20 s to respond), unless real-time or over 20 h |
| 2.2.2 | A | Pause, Stop, Hide | Carousels, marquees, auto-updating feeds lasting over 5 s have pause or stop controls |
| 2.3.1 | A | Three Flashes or Below Threshold | No content flashes more than 3 times per second |
| 2.4.1 | A | Bypass Blocks | Skip link to main content works, or landmarks/headings allow bypassing |
| 2.4.2 | A | Page Titled | Unique, descriptive `<title>`, updated on SPA route change |
| 2.4.3 | A | Focus Order | Tab order preserves meaning; dialogs receive focus; no positive `tabindex` chaos |
| 2.4.4 | A | Link Purpose (In Context) | Link text plus its sentence, paragraph or list item explains the destination; no bare "click here" lists |
| 2.4.5 | AA | Multiple Ways | At least two ways to reach pages (nav, search, sitemap), except steps in a process |
| 2.4.6 | AA | Headings and Labels | Headings and labels describe topic or purpose |
| 2.4.7 | AA | Focus Visible | Visible focus indicator on every focusable element |
| 2.4.11 | AA | Focus Not Obscured (Minimum) | Focused element is not entirely hidden by sticky headers, footers, banners or chat widgets |
| 2.5.1 | A | Pointer Gestures | Multipoint or path gestures (pinch, swipe) have single-pointer alternatives (buttons) |
| 2.5.2 | A | Pointer Cancellation | Actions fire on up-event, or can be aborted by moving off before release |
| 2.5.3 | A | Label in Name | Accessible name contains the visible label text (voice control users say what they see) |
| 2.5.4 | A | Motion Actuation | Shake/tilt functions have UI alternatives and can be disabled |
| 2.5.7 | AA | Dragging Movements | Drag operations (sortable lists, sliders, maps) have a single-pointer non-drag alternative |
| 2.5.8 | AA | Target Size (Minimum) | Targets at least 24x24 CSS px, or spaced so a 24 px circle around each does not overlap another; inline links and native controls exempt |

## 3 Understandable

| SC | Level | Name | How to test |
|---|---|---|---|
| 3.1.1 | A | Language of Page | Auto: `<html lang>` present and correct |
| 3.1.2 | AA | Language of Parts | Passages in another language carry `lang` |
| 3.2.1 | A | On Focus | Focusing an element does not submit, navigate or open a new window |
| 3.2.2 | A | On Input | Changing a select or checkbox does not trigger unexpected context changes without warning |
| 3.2.3 | AA | Consistent Navigation | Repeated navigation keeps the same relative order across pages |
| 3.2.4 | AA | Consistent Identification | Same function, same name and icon across pages |
| 3.2.6 | A | Consistent Help | Help links, contact details or chat appear in the same relative order on each page that has them |
| 3.3.1 | A | Error Identification | Errors are described in text and the field is identified |
| 3.3.2 | A | Labels or Instructions | Labels and required-format hints present before input |
| 3.3.3 | AA | Error Suggestion | Error messages say how to fix the problem when known |
| 3.3.4 | AA | Error Prevention (Legal, Financial, Data) | Submissions are reversible, checked, or confirmed before final commit |
| 3.3.7 | A | Redundant Entry | Previously entered info in the same process is auto-filled or selectable, unless re-entry is essential (e.g. password confirmation for security) |
| 3.3.8 | AA | Accessible Authentication (Minimum) | No cognitive function test to log in, unless an alternative, a mechanism to assist (paste, password manager, show password), or object/personal-content recognition is offered |

## 4 Robust

| SC | Level | Name | How to test |
|---|---|---|---|
| 4.1.2 | A | Name, Role, Value | Auto + screen reader: custom widgets expose role, name, state (`aria-expanded`, `aria-checked`, `aria-selected`) and update them |
| 4.1.3 | AA | Status Messages | Toasts, result counts, "saved", cart updates announced via `role="status"`/`aria-live` without moving focus |

## Coverage table format

```markdown
| SC | Result | Notes |
|---|---|---|
| 1.1.1 | Fail | Findings 3, 8 |
| 1.2.2 | Not applicable | No video content in scope |
| 2.5.8 | Pass | Spot-checked 40 controls at 1x zoom |
| 1.4.10 | Not tested | Checkout iframe provided by payment vendor |
```

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
New

Introducing Codex Skills Pro

79 professional skills that take Codex through complete engagements, each with its own scripts, references and a report that verifies the result.

All-AccessOne-time payment, no subscription
$49
Get All-AccessOr browse individual skills from $2.99 →

Secure checkout by Creem