a11y-playwright-testing
Accessibility testing for web applications using Playwright (@playwright/test) with TypeScript and axe-core. Use when asked to write, run, or debug automated accessibility checks, keyboard navigation tests, focus management, ARIA/semantic validations, screen reader compatibility, or WCAG 2.1 Level AA compliance testing. Covers axe-core integration, POUR principles (perceivable, operable, understandable, robust), color contrast, form labels, landmarks, and accessible names.
What this skill does
# Playwright Accessibility Testing (TypeScript)
Comprehensive toolkit for automated accessibility testing using Playwright with TypeScript and axe-core. Enables WCAG 2.1 Level AA compliance verification, keyboard operability testing, semantic validation, and accessibility regression prevention.
> **Activation:** This skill is triggered when working with accessibility testing, WCAG compliance, axe-core scans, keyboard navigation tests, focus management, ARIA validation, or screen reader compatibility.
## When to Use This Skill
- **Automated a11y scans** with axe-core for WCAG 2.1 AA compliance
- **Keyboard navigation tests** for Tab/Enter/Space/Escape/Arrow key operability
- **Focus management** validation for dialogs, menus, and dynamic content
- **Semantic structure** assertions for landmarks, headings, and ARIA
- **Form accessibility** testing for labels, errors, and instructions
- **Color contrast** and visual accessibility verification
- **Screen reader** compatibility testing patterns
## Prerequisites
| Requirement | Details |
| ----------- | ------------------------------ |
| Node.js | v18+ recommended |
| Playwright | `@playwright/test` installed |
| axe-core | `@axe-core/playwright` package |
| TypeScript | Configured in project |
### Quick Setup
```bash
# Add axe-core to existing Playwright project
npm install -D @axe-core/playwright axe-core
```
## First Questions to Ask
Before writing accessibility tests, clarify:
1. **Scope**: Which pages/flows are in scope? What's explicitly excluded?
2. **Standard**: WCAG 2.1 AA (default) or specific organizational policy?
3. **Priority**: Which components are highest risk (forms, modals, navigation, checkout)?
4. **Exceptions**: Known constraints (legacy markup, third-party widgets)?
5. **Assistive Tech**: Which screen readers/browsers need manual testing?
---
## Core Principles
### 1. Automation Limitations
> ⚠️ **Critical**: Automated tooling can detect ~30-40% of accessibility issues. Use automation to prevent regressions and catch common failures; **manual audits are required** for full WCAG conformance.
### 2. Semantic HTML First
Prefer native HTML semantics over ARIA. Use ARIA only when native elements cannot achieve the required semantics.
```typescript
// ✅ Semantic HTML - inherently accessible
await page.getByRole("button", { name: "Submit" }).click();
// ❌ ARIA override - requires manual keyboard/focus handling
await page.locator('[role="button"]').click(); // Often a <div>
```
### 3. Locator Strategy as A11y Signal
If you **cannot locate an element by role or label**, it's often an accessibility defect.
| Locator Success | Accessibility Signal |
| -------------------------------------------- | -------------------------- |
| `getByRole('button', { name: 'Submit' })` ✅ | Button has accessible name |
| `getByLabel('Email')` ✅ | Input properly labeled |
| `getByRole('navigation')` ✅ | Landmark exists |
| `locator('.submit-btn')` ⚠️ | May lack accessible name |
---
## Key Workflows
### Automated Axe Scan (WCAG 2.1 AA)
```typescript
import AxeBuilder from "@axe-core/playwright";
import { test, expect } from "@playwright/test";
test("page has no WCAG 2.1 AA violations", async ({ page }) => {
await page.goto("/");
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
expect(results.violations).toEqual([]);
});
```
### Scoped Axe Scan (Component-Level)
```typescript
test("form component is accessible", async ({ page }) => {
await page.goto("/contact");
const results = await new AxeBuilder({ page })
.include("#contact-form") // Scope to specific component
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
expect(results.violations).toEqual([]);
});
```
### Keyboard Navigation Test
```typescript
test("form is keyboard navigable", async ({ page }) => {
await page.goto("/login");
// Tab to first field
await page.keyboard.press("Tab");
await expect(page.getByLabel("Email")).toBeFocused();
// Tab to password
await page.keyboard.press("Tab");
await expect(page.getByLabel("Password")).toBeFocused();
// Tab to submit button
await page.keyboard.press("Tab");
await expect(page.getByRole("button", { name: "Sign in" })).toBeFocused();
// Submit with Enter
await page.keyboard.press("Enter");
await expect(page).toHaveURL(/dashboard/);
});
```
### Dialog Focus Management
```typescript
test("dialog traps and returns focus", async ({ page }) => {
await page.goto("/settings");
const trigger = page.getByRole("button", { name: "Delete account" });
// Open dialog
await trigger.click();
const dialog = page.getByRole("dialog");
await expect(dialog).toBeVisible();
// Focus should be inside dialog
await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused();
// Tab should stay trapped in dialog
await page.keyboard.press("Tab");
await expect(dialog.getByRole("button", { name: "Confirm" })).toBeFocused();
await page.keyboard.press("Tab");
await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused();
// Escape closes and returns focus to trigger
await page.keyboard.press("Escape");
await expect(dialog).toBeHidden();
await expect(trigger).toBeFocused();
});
```
### Skip Link Validation
```typescript
test("skip link moves focus to main content", async ({ page }) => {
await page.goto("/");
// First Tab should focus skip link
await page.keyboard.press("Tab");
const skipLink = page.getByRole("link", { name: /skip to (main|content)/i });
await expect(skipLink).toBeFocused();
// Activating skip link moves focus to main
await page.keyboard.press("Enter");
await expect(page.locator('#main, [role="main"]').first()).toBeFocused();
});
```
---
## POUR Principles Reference
| Principle | Focus Areas | Example Tests |
| ------------------ | ----------------------------------------- | ------------------------------------------------- |
| **Perceivable** | Alt text, captions, contrast, structure | Image alternatives, color contrast ratio |
| **Operable** | Keyboard, focus, timing, navigation | Tab order, focus visibility, skip links |
| **Understandable** | Labels, instructions, errors, consistency | Form labels, error messages, predictable behavior |
| **Robust** | Valid HTML, ARIA, name/role/value | Semantic structure, accessible names |
---
## Axe-Core Tags Reference
| Tag | WCAG Level | Use Case |
| --------------- | ------------ | -------------------------- |
| `wcag2a` | Level A | Minimum compliance |
| `wcag2aa` | Level AA | **Standard target** |
| `wcag2aaa` | Level AAA | Enhanced (rarely full) |
| `wcag21a` | 2.1 Level A | WCAG 2.1 specific A |
| `wcag21aa` | 2.1 Level AA | **WCAG 2.1 standard** |
| `best-practice` | Beyond WCAG | Additional recommendations |
### Default Tags (WCAG 2.1 AA)
```typescript
const WCAG21AA_TAGS = ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"];
```
---
## Exception Handling
When exceptions are unavoidable:
1. **Scope narrowly** - specific component/route only
2. **Document impact** - which WCAG criterion, user impact
3. **Set expiration** - owner + remediation date
4. **Track ticket** - link to remediation issue
```typescript
// ❌ Avoid: Global rule disable
new AxeBuilder({ page }).disableRules(["color-contrast"]);
// ✅ Better: Scoped exclusion with documentation
new AxeBuilder({ page })
.exclude("#third-party-widget") // Known issue: JIRA-1234, fix by Q2
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
```
---Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.