WordPress Accessibility Audit: WCAG 2.2 Compliance Checklist for Themes

WCAG 2.2 (published 2023) adds nine new success criteria to the existing 2.1 standard, with the most impactful changes for WordPress themes being: 2.4.11 Focus Appearance (focus indicators must be at least 2px thick with 3:1 contrast), 2.4.12 Focus Not Obscured (sticky headers must not completely cover focused elements), and 3.2.6 Consistent Help (help links must be in a consistent location). Running an automated audit is a starting point, not a complete test — manual keyboard and screen reader testing is always required.

Problem: A WordPress site launched without an accessibility audit — form labels are missing, color contrast fails WCAG AA, skip links are absent, and keyboard navigation is broken in custom components — but there is no structured process for identifying and prioritising fixes.

Solution: Run an automated audit with axe-core (via the axe DevTools browser extension or @axe-core/cli) to catch roughly 30–40% of WCAG 2.2 issues automatically. Supplement with a manual keyboard-only navigation pass and a screen reader test using NVDA + Firefox or VoiceOver + Safari. Prioritise Level A failures first, then Level AA. Fix missing labels, insufficient contrast (use a contrast checker API), and ARIA misuse in custom widgets.


The code and commands below run an automated axe-core audit via Playwright, add WCAG 2.2-compliant focus styles to a WordPress theme, and implement the focus-not-obscured pattern for sticky headers using the new CSS scroll-margin-top.


# 1. Install axe-core Playwright integration
npm install --save-dev @axe-core/playwright playwright

# 2. Run automated accessibility audit against dev site
npx playwright test --config=accessibility.config.js


// accessibility.config.js
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

const PAGES = [
    '/',
    '/sample-page/',
    '/shop/',
    '/?s=test',
];

for ( const path of PAGES ) {
    test( `WCAG 2.2 AA — ${path}`, async ( { page } ) => {
        await page.goto( `http://localhost:8080${path}` );

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

        // Log violations for debugging
        if ( results.violations.length ) {
            console.log( JSON.stringify( results.violations, null, 2 ) );
        }

        expect( results.violations ).toHaveLength( 0 );
    } );
}


/* ── WCAG 2.2 — 2.4.11 Focus Appearance ─────────────────────────────────
   Focus indicator: ≥2px outline, ≥3:1 contrast with adjacent colours     */
:focus-visible {
    outline:        3px solid #0073aa;   /* 4.6:1 vs white */
    outline-offset: 2px;
    border-radius:  2px;
}

/* Dark background context (e.g., dark header) */
.site-header :focus-visible {
    outline-color: #fff;                 /* white on dark — high contrast */
}

/* ── WCAG 2.2 — 2.4.12 Focus Not Obscured ───────────────────────────────
   scroll-margin-top ensures the focused element scrolls clear of a
   sticky header (e.g., 80px tall sticky nav)                              */
:focus-visible {
    scroll-margin-top: 90px;   /* slightly more than the sticky header height */
}

/* ── Additional WCAG 2.2 patterns ────────────────────────────────────────
   2.5.8 Target Size Minimum: interactive targets ≥ 24×24 CSS pixels       */
.wp-block-button__link,
button,
[role="button"],
input[type="checkbox"],
input[type="radio"] {
    min-width:  44px;   /* 44×44 is recommended (2.5.5 AAA); 24×24 is AA   */
    min-height: 44px;
}

/* Inline links are exempt from target size if within a text block */


NOTE: Automated tools like axe-core catch approximately 30–40% of WCAG issues — the rest require manual testing; the most commonly missed issues are keyboard trap detection (can you navigate an entire page by keyboard without getting stuck?), meaningful reading order, and sufficient contrast on text overlaid on images, which no automated tool can reliably check.