Senior developer expert skill for E2E accessibility testing with Playwright and W3C ARIA APG patterns
Expert skill for writing comprehensive E2E accessibility tests with Playwright, following W3C ARIA Authoring Practices Guide (APG) patterns and WCAG 2.2 AAA compliance. This skill provides real browser testing capabilities that jsdom/Vitest cannot match.
# Install the skill
npx skills add rcellas/playwright-a11y
# Install Playwright dependencies
npm install -D @playwright/test @axe-core/playwright axe-core
# Install Playwright browsers
npx playwright install chromiumplaywright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:4200',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
webServer: {
command: 'npm run start',
url: 'http://localhost:4200',
reuseExistingServer: !process.env.CI,
},
});import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('Modal is accessible', async ({ page }) => {
// WCAG 4.1.2: Name, Role, Value
// APG: Dialog Pattern
// GIVEN: Modal is rendered
await page.goto('/modal-page');
await page.locator('button:has-text("Open Modal")').click();
const modal = page.locator('[role="dialog"]');
// THEN: Has correct ARIA attributes
await expect(modal).toHaveAttribute('aria-modal', 'true');
await expect(modal).toBeVisible();
// WHEN: User presses Escape
await page.keyboard.press('Escape');
// THEN: Modal closes (WCAG 2.1.2: No Keyboard Trap)
await expect(modal).not.toBeVisible();
});- SKILL.md - Complete skill documentation with all patterns and best practices
- WCAG 2.2 Reference - WCAG 2.2 success criteria with Playwright examples
- APG Patterns - All 17 W3C APG patterns with test contracts
- Common Mistakes - Common accessibility mistakes and how to test for them
Playwright provides real browser testing that jsdom/Vitest cannot:
| Feature | Playwright | jsdom/Vitest |
|---|---|---|
| Focus Management | β Real focus behavior | β Simulated |
| Keyboard Events | β Real browser events | |
| Visual Testing | β Computed styles | β Not available |
| Focus Trap | β Tab cycling | β Cannot test |
| Cross-browser | β Chrome/Firefox/Safari | β jsdom only |
All accessibility tests follow Gherkin format for clarity:
test('description', async ({ page }) => {
// WCAG X.X.X: Criterion name
// APG Pattern: Pattern name - Rule quote
// GIVEN: Initial state setup
await page.goto('/page');
// WHEN: User performs an action
await page.keyboard.press('Tab');
// THEN: Accessibility contract is verified
await expect(element).toBeFocused();
});This skill provides test examples for 17+ W3C ARIA APG patterns:
- Dialog (Modal) - Focus trap, Escape to close, focus return
- Tabs - Arrow key navigation, Home/End keys
- Combobox - Autocomplete, listbox navigation
- Menu Button - Dropdown menus, keyboard navigation
- Accordion - Expand/collapse, aria-expanded
- Checkbox - Three-state checkboxes
- Radio Group - Arrow key navigation
- Switch - Toggle controls
- Disclosure - Show/hide content
- Slider - Arrow key adjustments
- Alert - Screen reader announcements
- Alert Dialog - Modal alerts
- Listbox - Selectable lists
- Breadcrumb - Navigation trails
- Tooltip - Hover and focus tooltips
- Tree View - Hierarchical navigation
- Toolbar - Tool collections
This skill helps you test for all WCAG 2.2 levels:
- Level A - Minimum conformance
- Level AA - Standard conformance (most common)
- Level AAA - Highest conformance
- 2.4.11 Focus Not Obscured (Minimum) - AA
- 2.4.12 Focus Not Obscured (Enhanced) - AAA
- 2.5.7 Dragging Movements - AA
- 2.5.8 Target Size (Minimum) - AA (24x24 pixels)
- 3.2.6 Consistent Help - A
- 3.3.7 Redundant Entry - A
- 3.3.8 Accessible Authentication (Minimum) - AA
The skill automatically detects and fixes common issues:
- β Detects failures (a11y assertions, test environment, imports)
- β Applies minimal safe edits to code/config/spec files
- β Re-runs the same failing test scope
- β Repeats until green or blocked by missing product decision
- β Reports exactly what was changed and why
- β React - Full support with Playwright
- β Angular - Full support with Playwright
- β Vue - Full support with Playwright
- β Svelte - Full support with Playwright
- β Vanilla JS - Full support with Playwright
- β Chromium - Chrome, Edge, Brave
- β Firefox - Mozilla Firefox
- β WebKit - Safari
test('modal traps focus within dialog', async ({ page }) => {
// APG Dialog: "Tab and Shift+Tab do not move focus outside the dialog"
await page.goto('/modal-page');
await page.locator('button:has-text("Open Modal")').click();
const dialog = page.locator('[role="dialog"]');
const firstButton = dialog.locator('button').first();
const lastButton = dialog.locator('button').last();
// Focus last element
await lastButton.focus();
// Tab should wrap to first element
await page.keyboard.press('Tab');
await expect(firstButton).toBeFocused();
// Shift+Tab should wrap to last element
await page.keyboard.press('Shift+Tab');
await expect(lastButton).toBeFocused();
});test('tabs support arrow key navigation', async ({ page }) => {
// APG Tabs: "When focus is on a tab, ArrowRight moves focus to the next tab"
await page.goto('/tabs-page');
const tab1 = page.locator('[role="tab"]').nth(0);
const tab2 = page.locator('[role="tab"]').nth(1);
await tab1.focus();
await expect(tab1).toHaveAttribute('aria-selected', 'true');
// ArrowRight moves to next tab
await page.keyboard.press('ArrowRight');
await expect(tab2).toBeFocused();
await expect(tab2).toHaveAttribute('aria-selected', 'true');
});test('has no WCAG 2.2 AAA violations', async ({ page }) => {
// WCAG 2.2 AAA automated scan
await page.goto('/page');
const results = await new AxeBuilder({ page })
.withTags(['wcag2aaa', 'wcag21aaa', 'wcag22aaa'])
.analyze();
expect(results.violations).toEqual([]);
});This skill is based on:
MIT
- Test keyboard navigation first - Most accessibility issues are keyboard-related
- Use real browser testing - Playwright provides accurate focus and keyboard behavior
- Follow APG patterns - Don't reinvent the wheel
- Run axe-core scans - Catch automated violations early
- Test in multiple browsers - Accessibility behavior can vary
For issues or questions:
- Check the SKILL.md documentation
- Review Common Mistakes
- Consult APG Patterns
- Open an issue on GitHub
Remember: Accessibility is not optional. Every interactive component must be keyboard accessible and screen reader compatible.