Senior developer expert skill for accessibility testing with Vitest and W3C ARIA APG patterns
This skill provides comprehensive guidance for writing precise, non-flaky accessibility tests using Vitest. It covers both jsdom mode and Browser Mode, with deep expertise in:
- β W3C ARIA Authoring Practices Guide (APG) patterns
- β WCAG 2.2 AA/AAA compliance testing
- β Real browser testing with Vitest Browser Mode
- β Automated accessibility scans with vitest-axe
- β Focus management and keyboard navigation
- β Screen reader compatibility verification
# For jsdom mode (structural tests + axe scans)
npm install --save-dev vitest vitest-axe @testing-library/react jsdom
# For Browser Mode (interactive tests + keyboard navigation)
npm install --save-dev @vitest/browser playwright vitest-browser-react
npx playwright install chromium
# For Angular
npm install --save-dev vitest vitest-axe @testing-library/angular
npm install --save-dev @vitest/browser playwright vitest-browser-angularvitest.config.ts (Browser Mode - Recommended for a11y)
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
browser: {
enabled: true,
provider: 'playwright',
instances: [{ browser: 'chromium' }],
headless: true,
},
},
});import { page, userEvent } from 'vitest/browser';
import { render } from 'vitest-browser-react';
import { test, expect } from 'vitest';
test('Modal is accessible', async () => {
// WCAG 4.1.2: Name, Role, Value
// APG: Dialog Pattern
const { getByRole } = render(<Modal isOpen title="Settings" />);
// Given: Modal is rendered
const modal = getByRole('dialog');
// Then: Has correct ARIA attributes
await expect.element(modal).toHaveAttribute('aria-modal', 'true');
await expect.element(modal).toHaveAccessibleName('Settings');
// When: User presses Escape
await userEvent.keyboard('{Escape}');
// Then: Modal closes (WCAG 2.1.2: No Keyboard Trap)
await expect.element(modal).not.toBeInTheDocument();
});- SKILL.md - Complete skill documentation with all patterns and best practices
- WCAG 2.2 Reference - WCAG 2.2 success criteria with examples
- APG Patterns - All 17 W3C APG patterns with test contracts
- Common Mistakes - 15 common mistakes and how to fix them
- Angular Examples - Complete Angular component tests
- React Examples - React component accessibility tests
- Vue Examples - Vue component accessibility tests
| Feature | Browser Mode | jsdom Mode |
|---|---|---|
| Focus Testing | β Real focus behavior | β Simulated |
| Keyboard Nav | β Real browser events | |
| Visual Testing | β Computed styles | β Not available |
| Speed | Slower (real browser) | Faster (simulated) |
| Use Case | Interactive a11y tests | Structural a11y tests |
Rule of Thumb:
- Browser Mode: For APG patterns (Dialog, Tabs, Combobox, etc.)
- jsdom Mode: For axe scans and semantic HTML verification
All tests follow Gherkin format for clarity:
test('description of what is being tested', async () => {
// WCAG X.X.X: Criterion name
// APG: "Specific rule from APG" (if applicable)
// Given: Setup - render component with specific state
const { container } = await render(Component, { props });
// When: Action - user interaction (if applicable)
await userEvent.keyboard('{Tab}');
// Then: Assertion - verify accessibility contract
await expect.element(element).toHaveFocus();
});| Pattern | Status | Documentation |
|---|---|---|
| Dialog (Modal) | β | APG Patterns |
| Tabs | β | APG Patterns |
| Combobox | β | APG Patterns |
| Menu Button | β | APG Patterns |
| Accordion | β | APG Patterns |
| Listbox | β | APG Patterns |
| Slider | β | APG Patterns |
| Tooltip | β | APG Patterns |
| Tree View | β | APG Patterns |
| Toolbar | β | APG Patterns |
| Breadcrumb | β | APG Patterns |
| Alert | β | APG Patterns |
| Alert Dialog | β | APG Patterns |
This skill covers all WCAG 2.2 Level AA criteria, including the 9 new success criteria:
- 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 CSS pixels)
- 3.2.6 Consistent Help - A
- 3.3.7 Redundant Entry - A
- 3.3.8 Accessible Authentication (Minimum) - AA
- 3.3.9 Accessible Authentication (Enhanced) - AAA
See WCAG 2.2 Reference for complete details.
The skill automatically detects and fixes common issues:
- β Detects failures (a11y assertions, test environment, imports, DI, aliases)
- β 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
- β Automatic detection of Angular projects
- β
Uses
ng test(required for Angular v20+) - β
Handles
templateUrl,styleUrls, and path aliases - β Automatic DI provider patching
- β React - Full support with vitest-browser-react
- β Angular - Full support with @testing-library/angular
- β Vue - Full support with vitest-browser-vue
- β Svelte - Full support with vitest-browser-svelte
test('Modal receives focus when opened', async () => {
// WCAG 2.4.3: Focus Order
// APG: "Focus moves to element inside dialog"
const { getByRole } = render(<App />);
const trigger = getByRole('button', { name: /open/i });
// When: User opens modal
await trigger.click();
// Then: Focus moves inside modal
const modal = getByRole('dialog');
await expect.element(modal).toHaveFocus();
});test('Tab key navigates through menu items', async () => {
// APG: "Tab β Moves focus to next focusable element"
// WCAG 2.1.1: Keyboard accessible
const { getByRole } = render(<Menu />);
const button = getByRole('button', { name: /menu/i });
// Given: Menu is open
await button.click();
// When: User presses Tab
await userEvent.keyboard('{Tab}');
// Then: Focus moves to first menu item
const firstItem = getByRole('menuitem', { name: /save/i });
await expect.element(firstItem).toHaveFocus();
});import { axe } from 'vitest-axe';
test('Component has no WCAG AA violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']
},
});
expect(results).toHaveNoViolations();
});We welcome contributions! Please see CONTRIBUTING.md for guidelines.
MIT License - see LICENSE file for details.
- Vitest Documentation
- Vitest Browser Mode
- W3C ARIA Authoring Practices Guide
- WCAG 2.2 Guidelines
- vitest-axe
- Testing Library
- Always read the component first - Never generate tests from a description alone
- Use Browser Mode for interactive tests - Focus, keyboard, and visual testing require a real browser
- Cite WCAG and APG rules - Every test should reference the specific criterion it verifies
- Follow Gherkin format - Given-When-Then makes tests readable and maintainable
- Test accessibility contracts, not implementation - Use semantic queries (
getByRole) over test IDs
- Issues: Report bugs or request features via GitHub Issues
- Discussions: Ask questions in GitHub Discussions
- Documentation: Check SKILL.md for complete reference