
Team Visual A11y
- 15 installs
- 2.1k repo stars
- Updated June 18, 2026
- catlog22/claude-code-workflow
Support for team-visual-a11y
About
Provides workflow support for team-visual-a11y. Solo builders use this to streamline development.
- team-visual-a11y
Team Visual A11y by the numbers
- 15 all-time installs (skills.sh)
- Ranked #2,109 of 3,282 Productivity & Planning skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/catlog22/claude-code-workflow --skill team-visual-a11yAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 15 |
|---|---|
| repo stars | ★ 2.1k |
| Last updated | June 18, 2026 |
| Repository | catlog22/claude-code-workflow ↗ |
What it does
Support for team-visual-a11y
Files
Team Visual Accessibility
Deep visual accessibility QA: OKLCH-based perceptual color contrast, typography readability at all viewports, focus-visible completeness, WCAG AA/AAA audit at rendered level. Built on team-worker agent architecture -- all worker roles share a single agent definition with role-specific Phase 2-4 loaded from roles/<role>/role.md.
Architecture
Skill(skill="team-visual-a11y", args="task description")
|
SKILL.md (this file) = Router
|
+--------------+--------------+
| |
no --role flag --role <name>
| |
Coordinator Worker
roles/coordinator/role.md roles/<name>/role.md
|
+-- analyze -> dispatch -> spawn workers -> STOP
|
+-------+-------+-------+
v v v |
[3 auditors spawn in PARALLEL] |
color-auditor typo-auditor focus-auditor
| | |
+---+---+---+---+
v
remediation-planner
|
v
fix-implementer (inner_loop)
|
v
[re-audit: color + focus in PARALLEL]Role Registry
| Role | Path | Prefix | Inner Loop |
|---|---|---|---|
| coordinator | roles/coordinator/role.md | -- | -- |
| color-auditor | roles/color-auditor/role.md | COLOR-* | false |
| typo-auditor | roles/typo-auditor/role.md | TYPO-* | false |
| focus-auditor | roles/focus-auditor/role.md | FOCUS-* | false |
| remediation-planner | roles/remediation-planner/role.md | REMED-* | false |
| fix-implementer | roles/fix-implementer/role.md | FIX-* | true |
Role Router
Parse $ARGUMENTS:
- Has
--role <name>-> Readroles/<name>/role.md, execute Phase 2-4 - No
--role->@roles/coordinator/role.md, execute entry router
Shared Constants
- Session prefix:
VA - Session path:
.workflow/.team/VA-<slug>-<date>/ - team_name:
visual-a11y - CLI tools:
ccw cli --mode analysis(read-only),ccw cli --mode write(modifications) - Message bus:
mcp__ccw-tools__team_msg(session_id=<session-id>, ...) - Max GC rounds: 2
Worker Spawn Template
Coordinator spawns workers using this template:
Agent({
subagent_type: "team-worker",
description: "Spawn <role> worker for <task-id>",
team_name: "visual-a11y",
name: "<role>",
run_in_background: true,
prompt: `## Role Assignment
role: <role>
role_spec: <skill_root>/roles/<role>/role.md
session: <session-folder>
session_id: <session-id>
team_name: visual-a11y
requirement: <task-description>
inner_loop: <true|false>
## Progress Milestones
session_id: <session-id>
Report progress via team_msg at natural phase boundaries (context loaded -> core work done -> verification).
Report blockers immediately via team_msg type="blocker".
Report completion via team_msg type="task_complete" after final SendMessage.
Read role_spec file (@<skill_root>/roles/<role>/role.md) to load Phase 2-4 domain instructions.
Execute built-in Phase 1 (task discovery) -> role Phase 2-4 -> built-in Phase 5 (report).`
})User Commands
| Command | Action |
|---|---|
check / status | View execution status graph |
resume / continue | Advance to next step |
Specs Reference
- specs/pipelines.md -- Pipeline definitions and task registry
- specs/oklch-standards.md -- OKLCH color accessibility rules
- specs/wcag-matrix.md -- WCAG 2.1 criteria matrix
- specs/typography-scale.md -- Typography accessibility rules
- specs/focus-patterns.md -- Focus management patterns
Session Directory
.workflow/.team/VA-<slug>-<date>/
+-- .msg/
| +-- messages.jsonl # Team message bus
| +-- meta.json # Pipeline config + GC state
+-- audits/
| +-- color/ # Color auditor output
| | +-- color-audit-001.md
| +-- typography/ # Typography auditor output
| | +-- typo-audit-001.md
| +-- focus/ # Focus auditor output
| +-- focus-audit-001.md
+-- remediation/ # Remediation planner output
| +-- remediation-plan.md
+-- fixes/ # Fix implementer output
| +-- fix-summary-001.md
+-- re-audit/ # Re-audit output (GC loop)
| +-- color-audit-002.md
| +-- focus-audit-002.md
+-- evidence/ # Screenshots, tracesError Handling
| Scenario | Resolution |
|---|---|
| Unknown command | Error with available command list |
| Role not found | Error with role registry |
| Session corruption | Attempt recovery, fallback to manual |
| Fast-advance conflict | Coordinator reconciles on next callback |
| Completion action fails | Default to Keep Active |
| GC loop stuck > 2 rounds | Escalate to user: accept / retry / terminate |
| Chrome DevTools unavailable | Degrade to static analysis only |
Color Accessibility Auditor
OKLCH-based perceptual color contrast analysis. Extract all color values, calculate WCAG 2.1 and APCA contrast ratios, assess OKLCH lightness/chroma ranges, simulate color blindness conditions. Produce detailed color audit report with pass/fail per combination.
Phase 2: Context & Environment Detection
| Input | Source | Required |
|---|---|---|
| Task description | From task subject/description | Yes |
| Session path | Extracted from task description | Yes |
| Target (URL or file paths) | From task description CONTEXT | Yes |
| WCAG level (AA/AAA) | From task description CONTEXT | Yes |
| .msg/meta.json | <session>/.msg/meta.json | No |
| Previous audit (re-audit) | <session>/audits/color/color-audit-*.md | Only for COLOR-002+ |
1. Extract session path, target, and WCAG level from task description 2. Determine audit type from subject: COLOR-001 -> initial audit, COLOR-002+ -> re-audit (verification) 3. Identify target:
- URL -> use Chrome DevTools for rendered colors (navigate_page, evaluate_script)
- File paths -> read CSS/SCSS/Tailwind config files directly
- Full site -> enumerate stylesheets from HTML entry points
4. For re-audit: read previous audit report and fix summary for comparison baseline
Phase 3: Color Audit Execution
Step 1: Color Extraction
Extract all color values from target:
Static analysis (always):
- Glob for CSS/SCSS/Tailwind files -> extract color definitions
- Parse CSS custom properties (--color-*), class colors, inline styles
- Normalize all formats to OKLCH for perceptual comparison:
#hex-> sRGB -> OKLCHrgb()/rgba()-> OKLCHhsl()/hsla()-> OKLCHoklch()-> direct- Record source location (file:line) for each color
Runtime analysis (if Chrome DevTools available):
mcp__chrome-devtools__navigate_page({ url: "<target-url>" })mcp__chrome-devtools__evaluate_script({ expression: "..." })with getComputedStyle to extract rendered colors from key elements (body, headings, links, buttons, inputs)mcp__chrome-devtools__take_screenshot({})for visual evidence -> save to<session>/evidence/
Step 2: Contrast Ratio Calculation
For each text/background color combination:
WCAG 2.1 Contrast Ratios:
- Calculate relative luminance:
L = 0.2126*R + 0.7152*G + 0.0722*B(linearized sRGB) - Contrast ratio:
(L1 + 0.05) / (L2 + 0.05)where L1 > L2 - Thresholds:
| Text Type | AA | AAA |
|---|---|---|
| Normal text (< 18pt / < 14pt bold) | >= 4.5:1 | >= 7:1 |
| Large text (>= 18pt / >= 14pt bold) | >= 3:1 | >= 4.5:1 |
| Non-text (UI components, icons) | >= 3:1 | >= 3:1 |
APCA Contrast (Lc values):
- Calculate APCA Lc using OKLCH lightness difference
- Thresholds:
| Use Case | Minimum Lc |
|---|---|
| Body text (16px) | >= 60 |
| Large text (24px+) | >= 45 |
| Non-text elements | >= 30 |
| Placeholder/disabled | >= 15 |
Step 3: OKLCH Lightness Analysis
Verify OKLCH lightness ranges per specs/oklch-standards.md:
| Element | Expected L Range | Flag If |
|---|---|---|
| Dark text on light bg | L <= 40% | L > 40% (too light for text) |
| Light background | L >= 90% | L < 90% (too dark for bg) |
| Accent colors | L 50-65% | Outside range |
| Disabled/muted text | L 55-70% | Outside range |
Check chroma values:
- Text colors: C near 0 (achromatic) unless intentional accent
- Vibrant accents: C 0.2-0.25, max 1-2 per palette
Step 4: Color Blindness Simulation
Assess color distinguishability under:
- Protanopia (red-blind): Check red/green pairs still distinguishable
- Deuteranopia (green-blind): Check green/red pairs
- Tritanopia (blue-blind): Check blue/yellow pairs
Flag combinations that rely solely on color difference without shape/text/pattern alternatives.
Step 5: Dark Mode Parity
If dark mode exists (media query or data-attribute):
- Verify all color combinations also pass in dark mode
- Check that OKLCH lightness relationships invert properly
- Flag combinations that pass in one mode but fail in the other
Dark Mode Audit (if dark mode exists)
| Check | Requirement | Method |
|---|---|---|
| No pure black background | Base bg uses tinted dark (oklch L >= 0.10, chroma >= 0.005) | evaluate_script: getComputedStyle check |
| Surface hierarchy | Higher elevation = lighter surface (at least 3 distinct levels) | Screenshot comparison |
| Font weight reduction | Dark theme reduces weight by 1 step vs light (600→500, 500→400) | Compare computed font-weight |
| Accent desaturation | Dark theme accents have lower OKLCH chroma than light (by 0.03-0.10) | Compare computed colors |
| Dangerous combos | No gray text on colored backgrounds, no red-green only indicators | Visual + computed color analysis |
| All contrast ratios met | WCAG AA requirements must pass in BOTH light and dark themes | Lighthouse audit both themes |
Phase 4: Report Generation & Output
1. Write audit report to <session>/audits/color/color-audit-{NNN}.md (or <session>/re-audit/color-audit-{NNN}.md for re-audits):
# Color Accessibility Audit - {NNN}
[color-auditor]
## Summary
- **Total color combinations tested**: {count}
- **Pass**: {pass_count} | **Fail**: {fail_count}
- **WCAG Level**: {AA|AAA}
- **Critical issues**: {count}
- **High issues**: {count}
## Color Palette (OKLCH)
| Name | Value | L% | C | H | Source |
|------|-------|----|---|---|--------|
| ... | oklch(X% Y Z) | X | Y | Z | file:line |
## Contrast Results
| Foreground | Background | WCAG Ratio | Pass/Fail | APCA Lc | Pass/Fail | Type |
|-----------|-----------|------------|-----------|---------|-----------|------|
| ... | ... | X.X:1 | PASS/FAIL | XX | PASS/FAIL | normal/large |
## OKLCH Lightness Issues
| Element | Current L | Expected Range | Status |
|---------|----------|---------------|--------|
| ... | X% | Y-Z% | PASS/FAIL |
## Color Blindness Assessment
| Combination | Protanopia | Deuteranopia | Tritanopia | Alt indicator |
|------------|-----------|-------------|-----------|--------------|
| ... | safe/risk | safe/risk | safe/risk | yes/no |
## Dark Mode Parity
| Combination | Light Mode | Dark Mode | Status |
|------------|-----------|----------|--------|
| ... | PASS/FAIL | PASS/FAIL | PASS/FAIL |
## Issues (by severity)
### Critical
- ...
### High
- ...
### Medium
- ...2. For re-audit (COLOR-002+), add before/after comparison section:
## Before/After Comparison
| Combination | Before (ratio) | After (ratio) | Status |
|------------|----------------|---------------|--------|
| ... | X.X:1 FAIL | Y.Y:1 PASS | FIXED |3. Update <session>/.msg/meta.json under color-auditor namespace:
- Read existing -> merge
{ "color-auditor": { audit_id, total_combinations, pass_count, fail_count, critical_count, high_count, timestamp } }-> write back
Analyze Task
Parse user task -> detect accessibility audit scope -> determine target -> select pipeline mode.
CONSTRAINT: Text-level analysis only. NO source code reading, NO codebase exploration.
Signal Detection
| Keywords | Capability | Pipeline Hint |
|---|---|---|
| color, contrast, oklch, palette, hue | color audit | audit-only or full |
| typography, font, line-height, clamp, readability | typography audit | audit-only or full |
| focus, tab, keyboard, aria, skip-link, focus-visible | focus audit | audit-only or full |
| wcag, a11y, accessibility, compliance | full audit | audit-only or full |
| fix, remediate, implement, correct | fix cycle | full |
| audit only, assessment, review, check | audit only | audit-only |
Target Detection
| Signal | Target Type |
|---|---|
| URL (http/https) | rendered-page |
| File path (.tsx, .vue, .html, .css) | component-source |
| "full site", "all pages", "entire app" | full-site |
| Component name without path | component-source (search needed) |
| Unclear | ask user |
Mode Determination
| Signal | Pipeline Mode |
|---|---|
| "audit only", "no fixes", "assessment", "review" | audit-only |
| "full", "fix", "remediate", "complete cycle" | full |
| Single audit domain (color OR typography OR focus only) | audit-only |
| Unclear | ask user |
WCAG Level Detection
| Signal | Level |
|---|---|
| "AA" or default | AA |
| "AAA", "enhanced", "strict" | AAA |
Complexity Scoring
| Factor | Points |
|---|---|
| Single component | +1 |
| Multiple components | +2 |
| Full site | +3 |
| Rendered page (Chrome DevTools) | +1 |
| AAA level requested | +1 |
| Fix cycle included | +1 |
Results: 1-2 Low, 3-4 Medium, 5+ High
Output
Write scope context to coordinator memory:
{
"pipeline_mode": "<audit-only|full>",
"target": {
"type": "<rendered-page|component-source|full-site>",
"value": "<URL or file path or 'all'>"
},
"wcag_level": "<AA|AAA>",
"scope": "<description>",
"complexity": { "score": 0, "level": "Low|Medium|High" },
"chrome_devtools": true
}Command: Dispatch
Create the visual accessibility task chain with correct dependencies and structured task descriptions. Supports audit-only and full pipeline modes.
Phase 2: Context Loading
| Input | Source | Required |
|---|---|---|
| User requirement | From coordinator Phase 1 | Yes |
| Session folder | From coordinator Phase 2 | Yes |
| Pipeline mode | From session meta.json pipeline | Yes |
| Target info | From session meta.json target | Yes |
1. Load user requirement and scope from meta.json 2. Load pipeline stage definitions from specs/pipelines.md 3. Read pipeline and target from meta.json
Phase 3: Task Chain Creation (Mode-Branched)
Task Description Template
Every task description uses structured format:
TaskCreate({
subject: "<TASK-ID>",
description: "PURPOSE: <what this task achieves> | Success: <measurable completion criteria>
TASK:
- <step 1: specific action>
- <step 2: specific action>
- <step 3: specific action>
CONTEXT:
- Session: <session-folder>
- Target: <URL or file path>
- WCAG Level: <AA|AAA>
- Upstream artifacts: <artifact-1>, <artifact-2>
- Shared memory: <session>/.msg/meta.json
EXPECTED: <deliverable path> + <quality criteria>
CONSTRAINTS: <scope limits, focus areas>"
})
TaskUpdate({ taskId: "<TASK-ID>", addBlockedBy: [<dependency-list>], owner: "<role>" })Mode Router
| Mode | Action |
|---|---|
audit-only | Create 4 tasks: [COLOR-001 + TYPO-001 + FOCUS-001] parallel -> REMED-001 |
full | Create 7 tasks: [COLOR-001 + TYPO-001 + FOCUS-001] -> REMED-001 -> FIX-001 -> [COLOR-002 + FOCUS-002] |
---
Audit-Only Pipeline Task Chain
COLOR-001 (color-auditor):
TaskCreate({
subject: "COLOR-001",
description: "PURPOSE: OKLCH color contrast audit for all color combinations | Success: Complete color audit with WCAG 2.1 + APCA contrast ratios and color blindness assessment
TASK:
- Extract all color values (OKLCH, HSL, RGB, hex) from stylesheets
- Calculate WCAG 2.1 contrast ratios for all text/background combinations
- Calculate APCA Lc values for body and large text
- Assess OKLCH lightness ranges for text and backgrounds
- Simulate color blindness (protanopia, deuteranopia, tritanopia)
- Check dark mode color parity if applicable
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Shared memory: <session>/.msg/meta.json
EXPECTED: <session>/audits/color/color-audit-001.md | Pass/fail per color combination
CONSTRAINTS: Read-only analysis | Use Chrome DevTools for computed styles when available"
})
TaskUpdate({ taskId: "COLOR-001", owner: "color-auditor" })TYPO-001 (typo-auditor):
TaskCreate({
subject: "TYPO-001",
description: "PURPOSE: Typography readability audit across all viewports | Success: Complete typography audit with size, line-height, reading width at each breakpoint
TASK:
- Audit font sizes at each breakpoint (320px, 768px, 1024px, 1400px)
- Validate minimum body text >= 16px on mobile
- Check line-height ratios (body 1.5-1.75, headings 1.1-1.3)
- Validate clamp() functions for responsive scaling
- Measure reading width (45-75 characters per line)
- Assess font loading strategy (font-display values)
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Shared memory: <session>/.msg/meta.json
EXPECTED: <session>/audits/typography/typo-audit-001.md | Breakpoint-by-breakpoint report
CONSTRAINTS: Read-only analysis | Screenshot at multiple viewports if Chrome DevTools available"
})
TaskUpdate({ taskId: "TYPO-001", owner: "typo-auditor" })FOCUS-001 (focus-auditor):
TaskCreate({
subject: "FOCUS-001",
description: "PURPOSE: Focus management and keyboard accessibility audit | Success: Complete focus audit with tab order, indicator visibility, ARIA coverage
TASK:
- Audit tab order for logical sequence and no tab traps
- Check focus indicator visibility (outline >= 2px, contrast >= 3:1)
- Verify :focus-visible usage for keyboard vs mouse distinction
- Check skip link presence and functionality
- Audit focus traps in modals/dialogs (Tab cycles, Escape dismisses)
- Verify ARIA live regions for dynamic content
- Check ARIA roles and states on interactive elements
- Validate keyboard operability (Enter/Space, Arrow keys)
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Shared memory: <session>/.msg/meta.json
EXPECTED: <session>/audits/focus/focus-audit-001.md | Element-by-element focus report
CONSTRAINTS: Read-only analysis | Tab through elements if Chrome DevTools available"
})
TaskUpdate({ taskId: "FOCUS-001", owner: "focus-auditor" })CRITICAL: COLOR-001, TYPO-001, FOCUS-001 have NO blockedBy -- they run in PARALLEL.
REMED-001 (remediation-planner):
TaskCreate({
subject: "REMED-001",
description: "PURPOSE: Synthesize all 3 audit findings into prioritized remediation plan | Success: Complete remediation plan with severity ranking, code-level fix guidance, WCAG criterion mapping
TASK:
- Read color, typography, and focus audit reports
- Merge and deduplicate findings
- Prioritize by severity (Critical > High > Medium > Low)
- Group by file/component for efficient fixing
- Provide code-level fix guidance (specific CSS/HTML changes)
- Map each fix to WCAG success criterion
- Estimate effort per fix
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Upstream artifacts: audits/color/color-audit-001.md, audits/typography/typo-audit-001.md, audits/focus/focus-audit-001.md
- Shared memory: <session>/.msg/meta.json
EXPECTED: <session>/remediation/remediation-plan.md | All critical/high issues addressed
CONSTRAINTS: Read-only synthesis | No code modifications"
})
TaskUpdate({ taskId: "REMED-001", addBlockedBy: ["COLOR-001", "TYPO-001", "FOCUS-001"], owner: "remediation-planner" })---
Full Pipeline Task Chain
Same as audit-only (COLOR-001, TYPO-001, FOCUS-001, REMED-001), plus:
FIX-001 (fix-implementer):
TaskCreate({
subject: "FIX-001",
description: "PURPOSE: Implement accessibility fixes from remediation plan | Success: All critical and high severity issues fixed with passing contrast ratios and ARIA validation
TASK:
- Read remediation plan for prioritized fix list
- Apply OKLCH color corrections for contrast compliance
- Add/fix focus styles (outline, outline-offset, :focus-visible)
- Add missing ARIA attributes (role, aria-label, aria-live, aria-expanded)
- Add reduced-motion media queries where needed
- Fix typography (clamp(), line-height, max-width for reading)
- Add skip link if missing
- Apply fixes in priority order (critical first)
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Upstream artifacts: remediation/remediation-plan.md
- Shared memory: <session>/.msg/meta.json
EXPECTED: Modified source files + <session>/fixes/fix-summary-001.md | All critical/high fixes applied
CONSTRAINTS: Modify only files identified in remediation plan | Preserve existing functionality"
})
TaskUpdate({ taskId: "FIX-001", addBlockedBy: ["REMED-001"], owner: "fix-implementer" })COLOR-002 (color-auditor):
TaskCreate({
subject: "COLOR-002",
description: "PURPOSE: Re-audit color contrast after fixes applied | Success: All color combinations pass WCAG target level
TASK:
- Re-extract all color values from modified stylesheets
- Re-calculate WCAG 2.1 contrast ratios
- Re-calculate APCA Lc values
- Compare before/after for each fixed combination
- Verify no regressions in unfixed combinations
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Upstream artifacts: fixes/fix-summary-001.md, audits/color/color-audit-001.md
- Shared memory: <session>/.msg/meta.json
EXPECTED: <session>/re-audit/color-audit-002.md | Before/after comparison with pass/fail
CONSTRAINTS: Read-only verification | Focus on fixed items + regression check"
})
TaskUpdate({ taskId: "COLOR-002", addBlockedBy: ["FIX-001"], owner: "color-auditor" })FOCUS-002 (focus-auditor):
TaskCreate({
subject: "FOCUS-002",
description: "PURPOSE: Re-audit focus management after fixes applied | Success: All focus indicators visible with correct ARIA attributes
TASK:
- Re-audit tab order after DOM changes
- Verify focus indicator fixes (outline, contrast)
- Check new ARIA attributes are valid
- Verify skip link implementation
- Test keyboard operability of fixed elements
- Compare before/after for each fixed element
CONTEXT:
- Session: <session-folder>
- Target: <target>
- WCAG Level: <level>
- Upstream artifacts: fixes/fix-summary-001.md, audits/focus/focus-audit-001.md
- Shared memory: <session>/.msg/meta.json
EXPECTED: <session>/re-audit/focus-audit-002.md | Before/after comparison with pass/fail
CONSTRAINTS: Read-only verification | Focus on fixed items + regression check"
})
TaskUpdate({ taskId: "FOCUS-002", addBlockedBy: ["FIX-001"], owner: "focus-auditor" })CRITICAL: COLOR-002 and FOCUS-002 both blocked only by FIX-001 -- they run in PARALLEL after fixes.
---
Phase 4: Validation
Verify task chain integrity:
| Check | Method | Expected |
|---|---|---|
| Task count correct | TaskList count | audit-only: 4, full: 7 |
| Dependencies correct | Trace dependency graph | Acyclic, correct blockedBy |
| No circular dependencies | Trace dependency graph | Acyclic |
| 3 audit tasks have NO blockedBy | Pattern check | COLOR-001, TYPO-001, FOCUS-001 are parallel |
| REMED-001 blocked by all 3 | Check blockedBy | [COLOR-001, TYPO-001, FOCUS-001] |
| Task IDs use correct prefixes | Pattern check | COLOR/TYPO/FOCUS/REMED/FIX |
| Structured descriptions complete | Each has PURPOSE/TASK/CONTEXT/EXPECTED/CONSTRAINTS | All present |
If validation fails, fix the specific task and re-validate.
Monitor Pipeline
Event-driven pipeline coordination. Beat model: coordinator wake -> process -> spawn -> STOP.
Constants
- SPAWN_MODE: background
- ONE_STEP_PER_INVOCATION: true
- FAST_ADVANCE_AWARE: true
- WORKER_AGENT: team-worker
- MAX_GC_ROUNDS: 2
Handler Router
| Source | Handler |
|---|---|
| Message contains [color-auditor], [typo-auditor], [focus-auditor], [remediation-planner], [fix-implementer] | handleCallback |
| "capability_gap" | handleAdapt |
| "check" or "status" | handleCheck |
| "resume" or "continue" | handleResume |
| All tasks completed | handleComplete |
| Default | handleSpawnNext |
handleCallback
Worker completed. Process and advance.
1. Parse message to identify role and task ID:
| Message Pattern | Role |
|---|---|
[color-auditor] or COLOR-* | color-auditor |
[typo-auditor] or TYPO-* | typo-auditor |
[focus-auditor] or FOCUS-* | focus-auditor |
[remediation-planner] or REMED-* | remediation-planner |
[fix-implementer] or FIX-* | fix-implementer |
2. Mark task completed: TaskUpdate({ taskId: "<task-id>", status: "completed" }) 3. Record completion in session state
4. Check checkpoint for completed task:
| Completed Task | Checkpoint | Action |
|---|---|---|
| COLOR-001 | Parallel fan-in | Check if all 3 audits complete -> unblock REMED-001 |
| TYPO-001 | Parallel fan-in | Check if all 3 audits complete -> unblock REMED-001 |
| FOCUS-001 | Parallel fan-in | Check if all 3 audits complete -> unblock REMED-001 |
| REMED-001 | -- | Unblock FIX-001 (full mode only) |
| FIX-001 | GC check | Spawn COLOR-002 + FOCUS-002 in parallel (full mode) |
| COLOR-002 | Re-audit fan-in | Check if both re-audits complete -> handleComplete |
| FOCUS-002 | Re-audit fan-in | Check if both re-audits complete -> handleComplete |
5. Parallel fan-in handling (audit task completed):
CRITICAL: REMED-001 must wait for ALL 3 auditors (COLOR-001 + TYPO-001 + FOCUS-001) to complete.
Check completion state:
completed_audits = count of completed tasks in [COLOR-001, TYPO-001, FOCUS-001]
if completed_audits < 3:
-> Log "[coordinator] Audit fan-in: {completed_audits}/3 complete, waiting for remaining"
-> STOP (do NOT advance)
if completed_audits === 3:
-> Log "[coordinator] All 3 audits complete, advancing to REMED-001"
-> handleSpawnNext (will pick up REMED-001)6. GC loop handling (full mode, after FIX-001 completes):
Read re-audit results when COLOR-002 and FOCUS-002 both complete:
| Signal | Condition | Action |
|---|---|---|
| All pass | No critical/high issues remaining | GC converged -> handleComplete |
| Issues remain | Critical/high issues found | gc_rounds < max -> create FIX-002 + re-audit tasks |
| Any | gc_rounds >= max | Escalate to user |
GC Fix Task Creation (when re-audit finds issues):
TaskCreate({ subject: "FIX-002",
description: "PURPOSE: Address remaining issues from re-audit | Success: All critical/high issues resolved
TASK:
- Parse re-audit reports for remaining issues
- Apply targeted fixes for color and focus issues
CONTEXT:
- Session: <session-folder>
- Upstream artifacts: re-audit/color-audit-002.md, re-audit/focus-audit-002.md" })
TaskUpdate({ taskId: "FIX-002", addBlockedBy: ["COLOR-002", "FOCUS-002"], owner: "fix-implementer" })Then create new re-audit tasks blocked by FIX-002. Increment gc_state.round.
GC Escalation Options (when max rounds exceeded): 1. Accept current state - acknowledge remaining issues 2. Try one more round 3. Terminate
7. -> handleSpawnNext
handleCheck
Read-only status report, then STOP.
Worker Progress (from message bus):
Before generating status output, read worker milestones:
const progressMsgs = mcp__ccw-tools__team_msg({
operation: "list", session_id: sessionId, type: "progress", last: 50
})
const blockerMsgs = mcp__ccw-tools__team_msg({
operation: "list", session_id: sessionId, type: "blocker", last: 10
})
// Aggregate latest milestone per task
const taskProgress = {}
for (const msg of (progressMsgs.result?.messages || [])) {
const tid = msg.data?.task_id
if (tid && (!taskProgress[tid] || msg.ts > taskProgress[tid].ts)) {
taskProgress[tid] = { phase: msg.data.phase, pct: msg.data.progress_pct, ts: msg.ts }
}
}Include in status output:
- Per-worker latest milestone (phase + progress_pct) next to task status
- Active blockers section (if any blockerMsgs found)
Pipeline Status (<pipeline-mode>):
[DONE] COLOR-001 (color-auditor) -> audits/color/color-audit-001.md
[DONE] TYPO-001 (typo-auditor) -> audits/typography/typo-audit-001.md
[RUN] FOCUS-001 (focus-auditor) -> auditing focus...
[WAIT] REMED-001 (remediation-planner) -> blocked by COLOR-001, TYPO-001, FOCUS-001
[WAIT] FIX-001 (fix-implementer) -> blocked by REMED-001
[WAIT] COLOR-002 (color-auditor) -> blocked by FIX-001
[WAIT] FOCUS-002 (focus-auditor) -> blocked by FIX-001
Fan-in: 2/3 audits complete
GC Rounds: 0/2
Session: <session-id>
Commands: 'resume' to advance | 'check' to refreshOutput status -- do NOT advance pipeline.
handleResume
1. Audit task list for inconsistencies:
- Tasks stuck in "in_progress" -> reset to "pending"
- Tasks with completed blockers but still "pending" -> include in spawn list
2. -> handleSpawnNext
handleSpawnNext
Find ready tasks, spawn workers, STOP.
1. Collect: completedSubjects, inProgressSubjects, readySubjects (pending + all blockedBy completed) 2. No ready + work in progress -> report waiting, STOP 3. No ready + nothing in progress -> handleComplete 4. Has ready -> for each: a. Check inner loop role with active worker -> skip (worker picks up) b. TaskUpdate -> in_progress c. team_msg log -> task_unblocked d. Spawn team-worker:
Agent({
subagent_type: "team-worker",
description: "Spawn <role> worker for <task-id>",
team_name: "visual-a11y",
name: "<role>",
run_in_background: true,
prompt: `## Role Assignment
role: <role>
role_spec: <skill_root>/roles/<role>/role.md
session: <session-folder>
session_id: <session-id>
team_name: visual-a11y
requirement: <task-description>
inner_loop: <true|false>
## Progress Milestones
session_id: <session-id>
Report progress via team_msg at natural phase boundaries (context loaded -> core work done -> verification).
Report blockers immediately via team_msg type="blocker".
Report completion via team_msg type="task_complete" after final SendMessage.
Read role_spec file to load Phase 2-4 domain instructions.
Execute built-in Phase 1 (task discovery) -> role Phase 2-4 -> built-in Phase 5 (report).`
})Parallel spawn rules by mode:
| Mode | Scenario | Spawn Behavior |
|---|---|---|
| audit-only | Initial | Spawn COLOR-001 + TYPO-001 + FOCUS-001 in parallel |
| audit-only | After 3 audits complete | Spawn REMED-001 |
| full | Initial | Spawn COLOR-001 + TYPO-001 + FOCUS-001 in parallel |
| full | After 3 audits complete | Spawn REMED-001 |
| full | After REMED-001 | Spawn FIX-001 |
| full | After FIX-001 | Spawn COLOR-002 + FOCUS-002 in parallel |
5. Add to active_workers, update session, output summary, STOP
handleComplete
Pipeline done. Generate report and completion action.
Completion check by mode:
| Mode | Completion Condition |
|---|---|
| audit-only | All 4 tasks (+ any fix tasks) completed |
| full | All 7 tasks (+ any GC fix tasks) completed |
1. If any tasks not completed -> handleSpawnNext 2. If all completed -> transition to coordinator Phase 5
handleAdapt
Capability gap reported mid-pipeline.
1. Parse gap description 2. Check if existing role covers it -> redirect 3. Role count < 6 -> generate dynamic role spec 4. Create new task, spawn worker 5. Role count >= 6 -> merge or pause
Fast-Advance Reconciliation
On every coordinator wake: 1. Read team_msg entries with type="fast_advance" 2. Sync active_workers with spawned successors 3. No duplicate spawns
Coordinator Role
Visual Accessibility Team coordinator. Orchestrate pipeline: analyze -> dispatch -> spawn -> monitor -> report. Manages parallel fan-in (3 auditors), remediation synthesis, fix implementation, and optional re-audit GC loop.
Identity
- Name: coordinator | Tag: [coordinator]
- Responsibility: Analyze scope -> Create team -> Dispatch tasks -> Monitor progress -> Report results
Boundaries
MUST
- All output (SendMessage, team_msg, logs) must carry
[coordinator]identifier - Use
team-workeragent type for all worker spawns (NOTgeneral-purpose) - Dispatch tasks with proper dependency chains and blockedBy
- Spawn COLOR-001, TYPO-001, FOCUS-001 in PARALLEL (no blockedBy between them)
- Monitor worker progress via message bus and route messages
- Handle Generator-Critic loops with max 2 iterations
- Maintain session state persistence
MUST NOT
- Implement domain logic (auditing, planning, fixing) -- workers handle this
- Spawn workers without creating tasks first
- Skip sync points when configured
- Force-advance pipeline past failed audit
- Modify source code or design artifacts directly -- delegate to workers
- Omit
[coordinator]identifier in any output
Command Execution Protocol
When coordinator needs to execute a command (analyze, dispatch, monitor):
1. Read commands/<command>.md 2. Follow the workflow defined in the command 3. Commands are inline execution guides, NOT separate agents 4. Execute synchronously, complete before proceeding
Entry Router
| Detection | Condition | Handler |
|---|---|---|
| Worker callback | Message contains [color-auditor], [typo-auditor], [focus-auditor], [remediation-planner], [fix-implementer] | -> handleCallback (monitor.md) |
| Status check | Args contain "check" or "status" | -> handleCheck (monitor.md) |
| Manual resume | Args contain "resume" or "continue" | -> handleResume (monitor.md) |
| Capability gap | Message contains "capability_gap" | -> handleAdapt (monitor.md) |
| Pipeline complete | All tasks have status "completed" | -> handleComplete (monitor.md) |
| Interrupted session | Active/paused session exists in .workflow/.team/VA-* | -> Phase 0 |
| New session | None of above | -> Phase 1 |
For callback/check/resume/adapt/complete: load @commands/monitor.md, execute matched handler, STOP.
Phase 0: Session Resume Check
1. Scan .workflow/.team/VA-*/.msg/meta.json for active/paused sessions 2. No sessions -> Phase 1 3. Single session -> reconcile (audit TaskList, reset in_progress->pending, rebuild team, kick first ready task) 4. Multiple -> AskUserQuestion for selection
Phase 1: Requirement Clarification
TEXT-LEVEL ONLY. No source code reading.
1. Parse task description from arguments 2. Detect audit scope:
| Signal | Pipeline Mode |
|---|---|
| "audit only", "no fixes", "assessment" | audit-only |
| "full audit", "fix", "remediate", "full cycle" | full |
| Unclear | ask user |
3. Ask for missing parameters if scope unclear:
AskUserQuestion({
questions: [
{ question: "Accessibility audit scope?", header: "Scope", options: [
{ label: "Audit only", description: "Color + typography + focus audit with remediation plan" },
{ label: "Full cycle", description: "Audit + fix + re-audit verification" }
]},
{ question: "Target?", header: "Target", options: [
{ label: "URL (rendered page)" },
{ label: "Component path (source)" },
{ label: "Full site" }
]}
]
})4. Delegate to @commands/analyze.md -> output scope context 5. Record: pipeline_mode, target, wcag_level
Phase 2: Create Team + Initialize Session
1. Resolve workspace paths (MUST do first):
project_root= result ofBash({ command: "pwd" })skill_root=<project_root>/.claude/skills/team-visual-a11y
2. Generate session ID: VA-<slug>-<YYYY-MM-DD> 3. Create session folder structure:
.workflow/.team/VA-<slug>-<date>/audits/color/
.workflow/.team/VA-<slug>-<date>/audits/typography/
.workflow/.team/VA-<slug>-<date>/audits/focus/
.workflow/.team/VA-<slug>-<date>/remediation/
.workflow/.team/VA-<slug>-<date>/fixes/
.workflow/.team/VA-<slug>-<date>/re-audit/
.workflow/.team/VA-<slug>-<date>/evidence/
.workflow/.team/VA-<slug>-<date>/.msg/4. Initialize .msg/meta.json via team_msg state_update with pipeline metadata 5. TeamCreate(team_name="visual-a11y") 6. Do NOT spawn workers yet - deferred to Phase 4
Phase 3: Create Task Chain
Delegate to @commands/dispatch.md. Task chains by mode:
| Mode | Task Chain |
|---|---|
| audit-only | [COLOR-001 + TYPO-001 + FOCUS-001 parallel] -> REMED-001 |
| full | [COLOR-001 + TYPO-001 + FOCUS-001 parallel] -> REMED-001 -> FIX-001 -> [COLOR-002 + FOCUS-002 parallel] |
Phase 4: Spawn-and-Stop
CRITICAL: Spawn COLOR-001, TYPO-001, FOCUS-001 in PARALLEL (all 3 have NO blockedBy).
Delegate to @commands/monitor.md#handleSpawnNext: 1. Find ready tasks (pending + blockedBy resolved) 2. Spawn team-worker agents (see SKILL.md Spawn Template) 3. Output status summary 4. STOP
Phase 5: Report + Completion Action
1. Read session state -> collect all results 2. List deliverables:
| Deliverable | Path |
|---|---|
| Color Audit | <session>/audits/color/color-audit-001.md |
| Typography Audit | <session>/audits/typography/typo-audit-001.md |
| Focus Audit | <session>/audits/focus/focus-audit-001.md |
| Remediation Plan | <session>/remediation/remediation-plan.md |
| Fix Summary | <session>/fixes/fix-summary-001.md (full mode) |
| Re-audit Color | <session>/re-audit/color-audit-002.md (full mode) |
| Re-audit Focus | <session>/re-audit/focus-audit-002.md (full mode) |
| Evidence | <session>/evidence/*.png (if Chrome DevTools used) |
3. Calculate: completed_tasks, gc_rounds, issues_found, issues_fixed, wcag_compliance_level 4. Output pipeline summary with [coordinator] prefix 5. Execute completion action:
AskUserQuestion({
questions: [{ question: "Pipeline complete. What next?", header: "Completion", options: [
{ label: "Archive & Clean", description: "Archive session and clean up team resources" },
{ label: "Keep Active", description: "Keep session for follow-up work" },
{ label: "Export Results", description: "Export deliverables to specified location" }
]}]
})Error Handling
| Error | Resolution |
|---|---|
| Task timeout | Log, mark failed, ask user to retry or skip |
| Worker crash | Reset task to pending, respawn worker |
| Dependency cycle | Detect, report to user, halt |
| Invalid scope | Reject with error, ask to clarify |
| Session corruption | Attempt recovery, fallback to manual reconciliation |
| GC loop stuck > 2 rounds | Escalate to user: accept / try one more / terminate |
| Chrome DevTools unavailable | Mark in meta.json, auditors degrade to static analysis |
Accessibility Fix Implementer
Implement accessibility fixes from the remediation plan. OKLCH color corrections, focus styles, ARIA attributes, reduced-motion queries, typography adjustments. Apply fixes in priority order (critical first) and self-validate results.
Phase 2: Context & Artifact Loading
| Input | Source | Required |
|---|---|---|
| Task description | From task subject/description | Yes |
| Session path | Extracted from task description | Yes |
| Remediation plan | <session>/remediation/remediation-plan.md | Yes |
| Color audit report | <session>/audits/color/color-audit-001.md | Yes |
| Focus audit report | <session>/audits/focus/focus-audit-001.md | Yes |
| Typography audit report | <session>/audits/typography/typo-audit-001.md | Yes |
| .msg/meta.json | <session>/.msg/meta.json | Yes |
| Re-audit reports (GC loop) | <session>/re-audit/*.md | Only for FIX-002+ |
1. Extract session path from task description 2. Read remediation plan -- this is the primary work instruction 3. Parse priority matrix for ordered fix list 4. Read audit reports for detailed context on each issue 5. For FIX-002+ (GC loop): read re-audit reports for remaining issues
Phase 3: Fix Implementation
Apply fixes in priority order: Critical -> High -> Medium -> Low.
Category 1: OKLCH Color Corrections
Goal: Adjust OKLCH lightness/chroma to meet contrast requirements.
Process: 1. Read remediation plan for specific color changes needed 2. Locate CSS custom properties or color definitions in source files 3. Apply lightness adjustments:
- Text too light -> decrease L% (e.g., L 55% -> L 35%)
- Background too dark -> increase L% (e.g., L 80% -> L 92%)
- Maintain hue (H) and adjust chroma (C) minimally
4. Verify new contrast ratios meet WCAG target 5. If dark mode exists, apply corresponding adjustments
Example:
/* Before: contrast 3.2:1 */
--color-text: oklch(55% 0 0);
--color-bg: oklch(98% 0 0);
/* After: contrast 7.2:1 (AAA) */
--color-text: oklch(25% 0 0);
--color-bg: oklch(98% 0 0);Category 2: Focus Styles
Goal: Add visible, high-contrast focus indicators for keyboard users.
Process: 1. Add :focus-visible rules per specs/focus-patterns.md:
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
:focus:not(:focus-visible) {
outline: none;
}2. Remove bare outline: none or outline: 0 that suppress focus without alternative 3. Verify focus indicator contrast >= 3:1 against adjacent colors 4. Add consistent focus style across all interactive elements
Category 3: ARIA Attributes
Goal: Add missing ARIA roles, states, and properties.
Process: 1. Add role attributes where semantic HTML is not used:
<div onclick>-> addrole="button"andtabindex="0"- Custom widgets -> appropriate ARIA role
2. Add state attributes:
- Toggle buttons:
aria-pressed="true|false" - Expandable:
aria-expanded="true|false",aria-controls="panel-id" - Dialogs:
role="dialog",aria-modal="true",aria-labelledby="title-id"
3. Add labels:
aria-labelfor icon-only buttonsaria-labelledbyfor dialogs and sectionsaria-describedbyfor form error messages
4. Add live regions:
- Status messages:
aria-live="polite" - Error messages:
aria-live="assertive"
Category 4: Reduced-Motion Queries
Goal: Respect user's motion preferences.
Process: 1. Wrap animations and transitions in media query:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}2. Or per-element: disable specific animations while keeping layout 3. Ensure no content is lost when motion is reduced
Category 5: Typography Adjustments
Goal: Fix font sizes, line heights, and reading widths.
Process: 1. Replace fixed font sizes with responsive clamp():
body { font-size: clamp(1rem, 1vw + 0.875rem, 1.25rem); }2. Fix line-height to acceptable ranges:
body { line-height: 1.625; }
h1, h2, h3 { line-height: 1.2; }3. Add max-width for reading containers:
.content { max-width: 70ch; }Category 6: Skip Link
Goal: Add skip-to-main-content link if missing.
Process (per specs/focus-patterns.md): 1. Add as first child of <body>:
<a href="#main" class="skip-link">Skip to main content</a>2. Add CSS:
.skip-link {
position: absolute;
left: -9999px;
top: auto;
}
.skip-link:focus {
position: fixed;
left: 16px;
top: 16px;
z-index: 9999;
background: var(--color-paper, #fff);
color: var(--color-ink, #000);
padding: 8px 16px;
border-radius: 4px;
text-decoration: underline;
}3. Ensure main content has id="main" (or equivalent target)
Category 7: Focus Trap for Modals
Goal: Implement proper focus management in dialogs.
Process (per specs/focus-patterns.md): 1. On dialog open:
- Store
document.activeElementas return target - Move focus to first focusable element within dialog
2. Trap Tab/Shift+Tab within dialog:
- Collect all focusable elements within dialog
- On Tab at last element -> focus first element
- On Shift+Tab at first element -> focus last element
3. On Escape -> close dialog, restore focus to stored element 4. Set aria-modal="true" on dialog container 5. Optional: set inert on background content
Phase 4: Self-Validation & Output
1. Validate each fix category:
| Category | Validation Method |
|---|---|
| Color | Recalculate contrast ratios for changed colors, verify >= target |
| Focus | Search for :focus-visible rules, verify no bare outline:none |
| ARIA | Check all dialogs have role+aria-modal, all buttons have labels |
| Motion | Verify prefers-reduced-motion media query exists |
| Typography | Check clamp() minimum >= 1rem, line-height in range |
| Skip link | Verify skip link element exists, CSS hides then shows on focus |
| Focus trap | Verify dialog has focus management code |
2. Write fix summary to <session>/fixes/fix-summary-{NNN}.md:
# Fix Summary - {NNN}
[fix-implementer]
## Overview
- **Total fixes applied**: {count}
- **Critical fixed**: {count}
- **High fixed**: {count}
- **Medium fixed**: {count}
- **Files modified**: {count}
## Fixes Applied
### Fix 1: Color contrast (Critical, WCAG 1.4.3)
- **File**: `src/styles/globals.css:12`
- **Before**: `--color-text: oklch(55% 0 0)` (contrast 3.2:1)
- **After**: `--color-text: oklch(25% 0 0)` (contrast 7.2:1)
- **Validated**: PASS
### Fix 2: Focus indicators (High, WCAG 2.4.7)
- **File**: `src/styles/globals.css:45`
- **Before**: No :focus-visible rules
- **After**: Added :focus-visible with 2px outline
- **Validated**: PASS
...
## Files Modified
| File | Changes |
|------|---------|
| src/styles/globals.css | Color adjustments, focus styles, reduced-motion |
| src/components/Modal.tsx | Focus trap, ARIA attributes |
| src/app/layout.tsx | Skip link |
## Remaining Issues
- {Any medium/low issues not addressed in this round}
## Verification Status
- [ ] Color contrast: {n}/{total} passing
- [ ] Focus indicators: {n}/{total} visible
- [ ] ARIA coverage: {n}/{total} complete
- [ ] Typography: {n}/{total} compliant3. Update <session>/.msg/meta.json under fix-implementer namespace:
- Read existing -> merge
{ "fix-implementer": { fix_id, total_fixes, critical_fixed, high_fixed, medium_fixed, files_modified, timestamp } }-> write back
Focus & Keyboard Accessibility Auditor
Focus-visible completeness audit. Tab order, focus indicator visibility (3:1 contrast), skip link, focus traps, ARIA live regions, keyboard operability. Produce detailed focus audit report with element-by-element analysis.
Phase 2: Context & Environment Detection
| Input | Source | Required |
|---|---|---|
| Task description | From task subject/description | Yes |
| Session path | Extracted from task description | Yes |
| Target (URL or file paths) | From task description CONTEXT | Yes |
| WCAG level (AA/AAA) | From task description CONTEXT | Yes |
| .msg/meta.json | <session>/.msg/meta.json | No |
| Previous audit (re-audit) | <session>/audits/focus/focus-audit-*.md | Only for FOCUS-002+ |
1. Extract session path, target, and WCAG level from task description 2. Determine audit type from subject: FOCUS-001 -> initial audit, FOCUS-002+ -> re-audit (verification) 3. Identify target:
- URL -> use Chrome DevTools for interactive focus testing
- File paths -> read HTML/JSX/Vue/Svelte files for interactive elements
- Full site -> enumerate pages from navigation/routes
4. For re-audit: read previous audit report and fix summary for comparison baseline 5. Read focus patterns from specs/focus-patterns.md for reference
Phase 3: Focus Audit Execution
Step 1: Interactive Element Inventory
Static analysis (always):
- Glob for HTML/JSX/TSX/Vue/Svelte files
- Extract all interactive elements:
- Native focusable:
<a href>,<button>,<input>,<select>,<textarea>,<details> - Custom focusable: elements with
tabindex,role="button",role="link",role="tab", etc. - Modals/dialogs:
role="dialog",role="alertdialog",<dialog> - Dynamic content areas:
aria-live,role="status",role="alert" - Record source location (file:line) and context for each element
Runtime analysis (if Chrome DevTools available):
mcp__chrome-devtools__navigate_page({ url: "<target-url>" })mcp__chrome-devtools__evaluate_script({ expression: "..." })to:- Enumerate all focusable elements in DOM order
- Check
tabindexvalues (0, -1, positive) - Detect focus traps (elements that prevent Tab from escaping)
Step 2: Tab Order Audit
| Check | Criterion | Standard |
|---|---|---|
| Logical sequence | Tab order follows visual/reading order | WCAG 2.4.3 |
| No positive tabindex | tabindex > 0 is an anti-pattern | Best practice |
| No tab traps | Tab can escape all containers (except modals) | WCAG 2.1.2 |
| Skip link first | First focusable element is skip-to-content link | WCAG 2.4.1 |
| All interactive reachable | Every interactive element reachable via Tab | WCAG 2.1.1 |
Flag:
tabindex > 0(disrupts natural tab order)tabindex="-1"on elements that should be focusable- Missing
tabindex="0"on custom interactive elements outline: noneoroutline: 0without alternative focus indicator
Step 3: Focus Indicator Visibility
Per specs/focus-patterns.md:
| Check | Requirement | Standard |
|---|---|---|
| Outline present | At least 2px outline on :focus-visible | WCAG 2.4.7 |
| Outline contrast | >= 3:1 against adjacent colors | WCAG 2.4.11 |
| :focus-visible used | Keyboard focus distinct from mouse click | Best practice |
| Not obscured | Focus indicator not hidden by overlays, sticky headers | WCAG 2.4.11 |
| Consistent style | Same focus style across similar elements | Best practice |
CSS analysis:
- Search for
:focus,:focus-visible,:focus-withinrules - Check for
outline: none/outline: 0without alternative (box-shadow, border) - Verify
:focus:not(:focus-visible) { outline: none; }pattern (mouse click suppression) - Calculate focus indicator contrast against adjacent background
Step 4: Skip Link Audit
| Check | Requirement |
|---|---|
| Exists | <a href="#main"> or similar as first focusable element |
| Visually hidden until focus | position: absolute; left: -9999px pattern |
| Visible on focus | Repositions on :focus to visible area |
| Target exists | #main or target ID exists in DOM |
| Functional | Focus moves to main content on activation |
Step 5: Focus Trap Audit (Modals/Dialogs)
Per specs/focus-patterns.md:
| Check | Requirement | Standard |
|---|---|---|
| Focus moves to dialog | On open, focus moves to first focusable or dialog itself | ARIA Practices |
| Tab cycles within | Tab/Shift+Tab stay within dialog focusable elements | ARIA Practices |
| Escape closes | Escape key closes dialog | ARIA Practices |
| Focus restores | On close, focus returns to trigger element | ARIA Practices |
| Background inert | Background content not focusable while dialog open | Best practice |
| Scroll lock | Background scroll locked while dialog open | Best practice |
Step 6: ARIA Audit
| Element Type | Required ARIA | Standard |
|---|---|---|
| Buttons | role="button" (if not <button>), aria-pressed (toggle), aria-expanded (disclosure) | WCAG 4.1.2 |
| Dialogs | role="dialog", aria-modal="true", aria-labelledby | ARIA Practices |
| Navigation | role="navigation" (if not <nav>), aria-label for multiple nav regions | WCAG 4.1.2 |
| Live regions | aria-live="polite" (status), aria-live="assertive" (errors) | WCAG 4.1.3 |
| Forms | aria-required, aria-invalid, aria-describedby for errors | WCAG 4.1.2 |
| Tabs | role="tablist", role="tab", role="tabpanel", aria-selected | ARIA Practices |
| Accordions | aria-expanded, aria-controls | ARIA Practices |
Step 7: Keyboard Operability
| Element | Expected Keys | Standard |
|---|---|---|
| Links/buttons | Enter activates | WCAG 2.1.1 |
| Buttons | Space activates | WCAG 2.1.1 |
| Checkboxes | Space toggles | WCAG 2.1.1 |
| Radio groups | Arrow keys navigate within group | ARIA Practices |
| Tabs | Arrow keys switch tabs, Tab moves to panel | ARIA Practices |
| Menus | Arrow keys navigate, Enter selects, Escape closes | ARIA Practices |
| Sliders | Arrow keys adjust value | ARIA Practices |
Input Method Awareness
| Check | Requirement |
|---|---|
| Hover guard | :hover styles wrapped in @media(hover:hover) — touch devices don't hover |
| Pointer detection | @media(pointer:coarse) used for larger touch targets (48px+) |
| Focus-visible distinction | :focus-visible for keyboard only, not mouse clicks |
| Touch target sizing | All interactive elements >= 44x44px on pointer:coarse devices |
Step 8: Target Size (WCAG 2.5.8)
| Check | Requirement | Standard |
|---|---|---|
| Interactive elements | Minimum 24x24px CSS pixels | WCAG 2.5.8 AA |
| Inline links | Exempt if within text paragraph | Exception |
| Spacing | At least 24px between targets OR targets themselves >= 24px | WCAG 2.5.8 |
Phase 4: Report Generation & Output
1. Write audit report to <session>/audits/focus/focus-audit-{NNN}.md (or <session>/re-audit/focus-audit-{NNN}.md for re-audits):
# Focus & Keyboard Accessibility Audit - {NNN}
[focus-auditor]
## Summary
- **Total interactive elements**: {count}
- **Pass**: {pass_count} | **Fail**: {fail_count}
- **WCAG Level**: {AA|AAA}
- **Critical issues**: {count}
- **High issues**: {count}
## Tab Order
| # | Element | Type | tabindex | Logical | Status |
|---|---------|------|----------|---------|--------|
| 1 | Skip link | <a> | 0 | Yes | PASS |
| 2 | Logo link | <a> | 0 | Yes | PASS |
| ... | ... | ... | ... | ... | ... |
## Focus Indicators
| Element | :focus-visible | Outline | Contrast | Obscured | Status |
|---------|---------------|---------|----------|----------|--------|
| ... | yes/no | Xpx solid | X:1 | no | PASS/FAIL |
## Skip Link
| Check | Status | Details |
|-------|--------|---------|
| Exists | PASS/FAIL | ... |
| Hidden until focus | PASS/FAIL | ... |
| Target exists | PASS/FAIL | ... |
## Focus Traps (Modals/Dialogs)
| Dialog | Focus on open | Tab cycles | Escape closes | Focus restores | Status |
|--------|-------------|-----------|--------------|---------------|--------|
| ... | yes/no | yes/no | yes/no | yes/no | PASS/FAIL |
## ARIA Coverage
| Element | Role | Label | States | Status |
|---------|------|-------|--------|--------|
| ... | ... | ... | ... | PASS/FAIL |
## Keyboard Operability
| Element | Enter | Space | Arrows | Escape | Status |
|---------|-------|-------|--------|--------|--------|
| ... | yes/no | yes/no | n/a | n/a | PASS/FAIL |
## Target Size
| Element | Size | Min 24x24 | Status |
|---------|------|----------|--------|
| ... | WxH | yes/no | PASS/FAIL |
## Issues (by severity)
### Critical
- ...
### High
- ...
### Medium
- ...2. For re-audit (FOCUS-002+), add before/after comparison section:
## Before/After Comparison
| Element | Before | After | Status |
|---------|--------|-------|--------|
| ... | FAIL (no outline) | PASS (2px solid) | FIXED |3. Update <session>/.msg/meta.json under focus-auditor namespace:
- Read existing -> merge
{ "focus-auditor": { audit_id, total_elements, pass_count, fail_count, critical_count, high_count, skip_link_present, focus_traps_valid, timestamp } }-> write back
Remediation Planner
Synthesize findings from all 3 audit reports (color, typography, focus) into a prioritized remediation plan with code-level fix guidance. Map each issue to WCAG success criterion, estimate effort, and group by file/component for efficient fixing.
Phase 2: Context & Artifact Loading
| Input | Source | Required |
|---|---|---|
| Task description | From task subject/description | Yes |
| Session path | Extracted from task description | Yes |
| Color audit report | <session>/audits/color/color-audit-001.md | Yes |
| Typography audit report | <session>/audits/typography/typo-audit-001.md | Yes |
| Focus audit report | <session>/audits/focus/focus-audit-001.md | Yes |
| WCAG level (AA/AAA) | From task description CONTEXT | Yes |
| .msg/meta.json | <session>/.msg/meta.json | Yes |
1. Extract session path and WCAG level from task description 2. Read ALL 3 audit reports -- all must exist (REMED-001 is blocked by all 3 auditors) 3. Parse each report for:
- Issues by severity (Critical/High/Medium)
- Specific elements and file:line locations
- Current values vs required values
4. Read meta.json for auditor summary stats
Phase 3: Remediation Plan Synthesis
Step 1: Issue Collection & Deduplication
Collect all issues from 3 audit reports:
| Source | Issue Types |
|---|---|
| Color audit | Contrast failures, OKLCH range violations, color blindness risks, dark mode gaps |
| Typography audit | Size violations, line-height issues, reading width, clamp() failures, font loading |
| Focus audit | Missing focus indicators, tab order issues, missing ARIA, keyboard gaps, skip link, target size |
Deduplicate:
- Same element flagged by multiple auditors -> merge into single issue with multiple aspects
- Same CSS rule causing multiple failures -> group under single fix
Step 2: Severity Classification
| Severity | Criteria | Examples |
|---|---|---|
| Critical | Blocks usage for impaired users, WCAG A violation | No keyboard access, contrast < 2:1, focus trap with no escape |
| High | Degrades experience significantly, WCAG AA violation | Contrast 3:1-4.5:1 on body text, missing skip link, no focus indicator |
| Medium | Non-compliance but usable, WCAG AAA-only violation | Contrast between AA and AAA thresholds, suboptimal reading width |
| Low | Enhancement, best practice | Font loading optimization, letter-spacing refinement |
Step 3: Prioritization & Grouping
Group by file/component for efficient fixing:
File: src/styles/globals.css
[CRITICAL] Color: --text-primary contrast 3.2:1 (need 4.5:1) -> WCAG 1.4.3
[HIGH] Focus: No :focus-visible on buttons -> WCAG 2.4.7
[MEDIUM] Typography: line-height 1.4 on body (need 1.5) -> WCAG 1.4.12
File: src/components/Modal.tsx
[CRITICAL] Focus: No focus trap in modal -> WCAG 2.1.2
[HIGH] ARIA: Missing aria-modal="true" -> WCAG 4.1.2Priority order within each file: Critical -> High -> Medium -> Low.
Step 4: Code-Level Fix Guidance
For each issue, provide specific fix:
Color fixes:
/* Before: contrast 3.2:1 */
--text-primary: oklch(55% 0 0);
/* After: contrast 5.1:1 -- adjust lightness down */
--text-primary: oklch(35% 0 0);Focus fixes:
/* Add :focus-visible to interactive elements */
button:focus-visible,
a:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
button:focus:not(:focus-visible) {
outline: none;
}ARIA fixes:
<!-- Before -->
<div class="modal">
<!-- After -->
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">Typography fixes:
/* Before: fixed font-size */
body { font-size: 14px; }
/* After: responsive with floor */
body { font-size: clamp(1rem, 1vw + 0.875rem, 1.25rem); line-height: 1.625; }Step 5: Effort Estimation
| Effort | Criteria |
|---|---|
| Trivial (< 5 min) | Single CSS property change (contrast, outline) |
| Small (5-15 min) | Multiple CSS changes in one file, add ARIA attributes |
| Medium (15-60 min) | Focus trap implementation, skip link, keyboard navigation |
| Large (1-4 hours) | Responsive typography overhaul, dark mode color parity |
Step 6: WCAG Criterion Mapping
Map every issue to specific WCAG success criterion:
| Issue Domain | Common Criteria |
|---|---|
| Color contrast | 1.4.3 (AA), 1.4.6 (AAA), 1.4.11 (non-text) |
| Typography | 1.4.4 (resize), 1.4.12 (text spacing) |
| Focus indicators | 2.4.7 (visible), 2.4.11 (not obscured) |
| Keyboard | 2.1.1 (keyboard), 2.1.2 (no trap) |
| Tab order | 2.4.3 (focus order) |
| Skip link | 2.4.1 (bypass blocks) |
| ARIA | 4.1.2 (name/role/value) |
| Target size | 2.5.8 (target size) |
| Live regions | 4.1.3 (status messages) |
Phase 4: Validation & Output
1. Validate plan completeness:
| Check | Requirement |
|---|---|
| All critical issues addressed | Every critical issue has a fix |
| All high issues addressed | Every high issue has a fix |
| File grouping complete | Every fix maps to specific file:line |
| WCAG mapping complete | Every fix maps to WCAG criterion |
| No orphan issues | Every audit issue appears in plan |
2. Write remediation plan to <session>/remediation/remediation-plan.md:
# Remediation Plan
[remediation-planner]
## Executive Summary
- **Total issues**: {count} (Critical: {n}, High: {n}, Medium: {n}, Low: {n})
- **Files affected**: {count}
- **Estimated total effort**: {hours}
- **WCAG target**: {AA|AAA}
## Priority Matrix
| # | Severity | Domain | WCAG | File | Issue | Fix | Effort |
|---|----------|--------|------|------|-------|-----|--------|
| 1 | Critical | Color | 1.4.3 | globals.css:12 | Contrast 3.2:1 | L 55% -> 35% | Trivial |
| 2 | Critical | Focus | 2.1.2 | Modal.tsx:45 | No focus trap | Add trap logic | Medium |
| ... | ... | ... | ... | ... | ... | ... | ... |
## Fixes by File
### `src/styles/globals.css`
#### Fix 1: Color contrast (Critical, WCAG 1.4.3)
- **Current**: `--text-primary: oklch(55% 0 0);` (contrast 3.2:1)
- **Target**: `--text-primary: oklch(35% 0 0);` (contrast 5.1:1)
- **Effort**: Trivial
#### Fix 2: Focus indicators (High, WCAG 2.4.7)
- **Current**: No :focus-visible rules
- **Target**: Add :focus-visible with 2px outline
- **Effort**: Small
### `src/components/Modal.tsx`
#### Fix 3: Focus trap (Critical, WCAG 2.1.2)
- **Current**: No focus management
- **Target**: Implement focus trap (store activeElement, cycle Tab, Escape to close)
- **Effort**: Medium
## Implementation Order
1. All Critical fixes (blocks usage)
2. All High fixes (degrades experience)
3. Medium fixes (non-compliance)
4. Low fixes (enhancements)
## Verification Checklist
- [ ] All color combinations pass WCAG {level} contrast
- [ ] All interactive elements have visible focus indicators
- [ ] Tab order is logical with no traps (except modals)
- [ ] Skip link present and functional
- [ ] All ARIA roles and states correct
- [ ] Typography meets minimum sizes at all breakpoints3. Update <session>/.msg/meta.json under remediation-planner namespace:
- Read existing -> merge
{ "remediation-planner": { total_issues, critical, high, medium, low, files_affected, estimated_effort, timestamp } }-> write back
Typography Readability Auditor
Typography accessibility audit across all viewports. Font sizes at breakpoints, line-height ratios, clamp() validation, reading width measurement, font loading strategy assessment. Produce detailed typography report with breakpoint-by-breakpoint analysis.
Phase 2: Context & Environment Detection
| Input | Source | Required |
|---|---|---|
| Task description | From task subject/description | Yes |
| Session path | Extracted from task description | Yes |
| Target (URL or file paths) | From task description CONTEXT | Yes |
| WCAG level (AA/AAA) | From task description CONTEXT | Yes |
| .msg/meta.json | <session>/.msg/meta.json | No |
1. Extract session path, target, and WCAG level from task description 2. Identify target:
- URL -> use Chrome DevTools for rendered typography (navigate_page, resize_page, screenshot)
- File paths -> read CSS/SCSS files directly for typography definitions
- Full site -> enumerate stylesheets from HTML entry points
3. Read typography standards from specs/typography-scale.md for reference thresholds
Phase 3: Typography Audit Execution
Step 1: Typography Definition Extraction
Static analysis (always):
- Glob for CSS/SCSS/Tailwind files -> extract typography definitions
- Extract: font-family, font-size, line-height, letter-spacing, font-weight, font-display
- Parse clamp() functions:
clamp(min, preferred, max)-> validate min/max bounds - Parse @media queries for responsive breakpoints
- Record source location (file:line) for each rule
Runtime analysis (if Chrome DevTools available):
- Navigate to target URL
- For each breakpoint (320px, 768px, 1024px, 1400px):
mcp__chrome-devtools__resize_page({ width: <breakpoint>, height: 900 })mcp__chrome-devtools__evaluate_script({ expression: "..." })to measure:- Computed font-size on body, headings (h1-h6), paragraphs, captions
- Computed line-height on same elements
- Container width and character count per line
mcp__chrome-devtools__take_screenshot({})-> save to<session>/evidence/typo-{breakpoint}px.png
Step 2: Font Size Audit
| Breakpoint | Element | Minimum Size | Standard |
|---|---|---|---|
| 320px (mobile) | Body text | 16px | WCAG 1.4.4 |
| 320px (mobile) | Small/caption | 14px | Best practice |
| 768px (tablet) | Body text | 16px | WCAG 1.4.4 |
| 1024px (desktop) | Body text | 16px | WCAG 1.4.4 |
| 1400px (wide) | Body text | 16px | WCAG 1.4.4 |
clamp() validation:
- Minimum value >= 14px (absolute floor)
- Maximum value reasonable for element type (headings: max ~72px, body: max ~20px)
- Preferred value uses viewport unit (vw) for fluid scaling
- Check:
clamp(min, preferred, max)where min >= 14px for any text
200% zoom check (WCAG 1.4.4):
- At 200% zoom, no content should be clipped or lost
- Text should reflow without horizontal scrolling
Step 3: Line Height Audit
| Element Type | Expected Range | Standard |
|---|---|---|
| Body text | 1.5 - 1.75 | WCAG 1.4.12, readability |
| Headings | 1.1 - 1.3 | Visual impact |
| Code blocks | 1.5 - 1.7 | Scanning readability |
| Buttons/labels | 1.2 - 1.5 | UI element |
Check for unitless line-height (preferred over px/em for inheritance).
Step 4: Letter Spacing Audit
| Element Type | Expected Range | Standard |
|---|---|---|
| Display headings | -0.02em to 0 | Tight for visual |
| Body text | 0 (default) | Normal |
| Uppercase labels | 0.05em - 0.1em | Legibility |
| Monospace code | 0.08em - 0.15em | Wide for scanning |
WCAG 1.4.12 override test: Text must remain readable when user overrides:
- Line height to at least 1.5x font size
- Letter spacing to at least 0.12em
- Word spacing to at least 0.16em
Step 5: Reading Width Audit
Measure characters per line for body text containers:
| Metric | Optimal | Acceptable | Flag |
|---|---|---|---|
| Characters per line | 66ch | 45-75ch | < 45 or > 75 |
| Max container width | 65ch-75ch | Up to 900px | > 900px without column |
Check: max-width or width on body text containers.
Step 6: Font Loading Strategy
| Property | Good | Acceptable | Poor |
|---|---|---|---|
| Body font | font-display: swap | font-display: fallback | font-display: block or missing |
| Display font | font-display: optional | font-display: swap | font-display: block |
| Preload | Critical fonts preloaded | Some preloaded | None preloaded |
| Fallback stack | System font fallback defined | Generic fallback | No fallback |
Check for FOUT (Flash of Unstyled Text) / FOIT (Flash of Invisible Text) risks.
Phase 4: Report Generation & Output
1. Write audit report to <session>/audits/typography/typo-audit-{NNN}.md:
# Typography Accessibility Audit - {NNN}
[typo-auditor]
## Summary
- **Total typography rules audited**: {count}
- **Pass**: {pass_count} | **Fail**: {fail_count}
- **WCAG Level**: {AA|AAA}
- **Critical issues**: {count}
- **High issues**: {count}
## Font Size by Breakpoint
| Breakpoint | Element | Computed Size | Min Required | Status |
|-----------|---------|--------------|-------------|--------|
| 320px | body | Xpx | 16px | PASS/FAIL |
| 320px | h1 | Xpx | -- | OK |
| ... | ... | ... | ... | ... |
## clamp() Validation
| Rule | clamp() Value | Min | Max | Status |
|------|--------------|-----|-----|--------|
| .body | clamp(1rem, 2vw, 1.25rem) | 16px | 20px | PASS/FAIL |
| ... | ... | ... | ... | ... |
## Line Height Audit
| Element | Current | Expected Range | Status |
|---------|---------|---------------|--------|
| body | 1.6 | 1.5-1.75 | PASS |
| h1 | 1.2 | 1.1-1.3 | PASS |
| ... | ... | ... | ... |
## Letter Spacing Audit
| Element | Current | Expected Range | Status |
|---------|---------|---------------|--------|
| ... | ... | ... | ... |
## Reading Width
| Container | Width | Chars/Line | Optimal (45-75) | Status |
|-----------|-------|-----------|-----------------|--------|
| .content | 700px | ~68ch | Yes | PASS |
| ... | ... | ... | ... | ... |
## Font Loading
| Font | font-display | Preload | Fallback | Status |
|------|-------------|---------|----------|--------|
| ... | swap | yes/no | system | PASS/FAIL |
## Text Spacing Override Test (WCAG 1.4.12)
| Override | Applied | Content Readable | Status |
|---------|---------|-----------------|--------|
| line-height: 1.5x | Yes | Yes/No | PASS/FAIL |
| letter-spacing: 0.12em | Yes | Yes/No | PASS/FAIL |
| word-spacing: 0.16em | Yes | Yes/No | PASS/FAIL |
## Issues (by severity)
### Critical
- ...
### High
- ...
### Medium
- ...2. Update <session>/.msg/meta.json under typo-auditor namespace:
- Read existing -> merge
{ "typo-auditor": { audit_id, total_rules, pass_count, fail_count, critical_count, high_count, breakpoints_tested, timestamp } }-> write back
Focus Management Patterns
Reference guide for focus indicator styles, skip links, focus traps, and ARIA patterns. Used by focus-auditor and fix-implementer roles.
Focus Indicator Style
Recommended Pattern
/* Keyboard focus: visible outline */
:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
/* Mouse click: no outline */
:focus:not(:focus-visible) {
outline: none;
}Requirements
| Property | Minimum | Rationale |
|---|---|---|
| outline-width | 2px | Visibility at distance |
| outline-style | solid | Consistent rendering |
| outline-offset | 2px | Separation from element edge |
| Contrast vs adjacent | >= 3:1 | WCAG 2.4.11 |
Anti-Patterns (Do NOT)
/* BAD: Removes all focus indicators */
*:focus { outline: none; }
/* BAD: Removes focus without alternative */
button:focus { outline: 0; }
/* BAD: Only uses box-shadow (invisible in high contrast mode) */
:focus-visible { outline: none; box-shadow: 0 0 0 2px blue; }Correct Alternative Indicator
/* If not using outline, MUST provide visible alternative */
:focus-visible {
outline: 2px solid transparent; /* For Windows high contrast mode */
box-shadow: 0 0 0 2px var(--color-accent);
}Skip Link
HTML
<body>
<a href="#main" class="skip-link">Skip to main content</a>
<!-- navigation, header, etc. -->
<main id="main" tabindex="-1">
<!-- main content -->
</main>
</body>CSS
.skip-link {
position: absolute;
left: -9999px;
top: auto;
width: 1px;
height: 1px;
overflow: hidden;
z-index: -1;
}
.skip-link:focus {
position: fixed;
left: 16px;
top: 16px;
width: auto;
height: auto;
overflow: visible;
z-index: 9999;
background: var(--color-paper, #fff);
color: var(--color-ink, #000);
padding: 8px 16px;
border: 2px solid var(--color-ink, #000);
border-radius: 4px;
font-size: 1rem;
text-decoration: underline;
}Requirements
| Check | Requirement |
|---|---|
| Position | First focusable element in DOM |
| Default state | Visually hidden (not display:none or visibility:hidden) |
| Focus state | Visible, fixed position, high z-index |
| Target | Points to main content area with valid ID |
| Contrast | Link text meets 4.5:1 contrast against background |
Focus Trap (Modals/Dialogs)
Implementation Pattern
function trapFocus(dialog) {
const focusableSelector = [
'a[href]', 'button:not([disabled])', 'input:not([disabled])',
'select:not([disabled])', 'textarea:not([disabled])',
'[tabindex]:not([tabindex="-1"])'
].join(', ');
const focusableElements = dialog.querySelectorAll(focusableSelector);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
// Store trigger for focus restore
const trigger = document.activeElement;
// Move focus to first element
firstFocusable.focus();
dialog.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeDialog(dialog);
trigger.focus(); // Restore focus
return;
}
if (e.key !== 'Tab') return;
if (e.shiftKey) {
// Shift+Tab at first element -> wrap to last
if (document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
}
} else {
// Tab at last element -> wrap to first
if (document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
});
}Dialog HTML Pattern
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Dialog Title</h2>
<div class="dialog-content">
<!-- content -->
</div>
<div class="dialog-actions">
<button type="button">Cancel</button>
<button type="button">Confirm</button>
</div>
</div>Requirements
| Step | Action | Detail |
|---|---|---|
| Open | Store trigger | const trigger = document.activeElement |
| Open | Move focus | Focus first focusable element in dialog |
| Open | Lock background | document.body.style.overflow = 'hidden' or inert attribute |
| Open | Set ARIA | aria-modal="true" on dialog |
| Tab | Cycle within | Tab/Shift+Tab wrap within dialog focusable elements |
| Escape | Close + restore | Close dialog, restore focus to trigger |
| Close | Unlock background | Remove scroll lock / inert |
ARIA Patterns
Button Patterns
<!-- Standard button -->
<button type="button">Save</button>
<!-- Icon-only button (needs aria-label) -->
<button type="button" aria-label="Close dialog">
<svg><!-- X icon --></svg>
</button>
<!-- Toggle button -->
<button type="button" aria-pressed="false">Bold</button>
<!-- Disclosure button -->
<button type="button" aria-expanded="false" aria-controls="panel-1">
Show details
</button>
<div id="panel-1" hidden>Details content</div>Dialog Pattern
<div role="dialog" aria-modal="true" aria-labelledby="dlg-title" aria-describedby="dlg-desc">
<h2 id="dlg-title">Confirm Action</h2>
<p id="dlg-desc">Are you sure you want to proceed?</p>
<button type="button">Cancel</button>
<button type="button">Confirm</button>
</div>Live Region Patterns
<!-- Status updates (polite) -->
<div role="status" aria-live="polite">
3 items in cart
</div>
<!-- Error messages (assertive) -->
<div role="alert" aria-live="assertive">
Email address is invalid
</div>
<!-- Log/chat (polite, additions only) -->
<div role="log" aria-live="polite" aria-relevant="additions">
<!-- new messages appended here -->
</div>Navigation Pattern
<!-- Primary navigation -->
<nav aria-label="Primary">
<ul role="menubar">
<li role="none"><a role="menuitem" href="/">Home</a></li>
<li role="none"><a role="menuitem" href="/about">About</a></li>
</ul>
</nav>
<!-- Breadcrumb -->
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li aria-current="page">Widget</li>
</ol>
</nav>Tab Pattern
<div role="tablist" aria-label="Settings">
<button role="tab" aria-selected="true" aria-controls="panel-general" id="tab-general">
General
</button>
<button role="tab" aria-selected="false" aria-controls="panel-security" id="tab-security" tabindex="-1">
Security
</button>
</div>
<div role="tabpanel" id="panel-general" aria-labelledby="tab-general">
General settings content
</div>
<div role="tabpanel" id="panel-security" aria-labelledby="tab-security" hidden>
Security settings content
</div>Keyboard: Arrow Left/Right to switch tabs, Tab to move into panel content.
Form Error Pattern
<label for="email">Email</label>
<input
type="email"
id="email"
aria-required="true"
aria-invalid="true"
aria-describedby="email-error"
>
<div id="email-error" role="alert">
Please enter a valid email address
</div>Keyboard Navigation Reference
| Component | Key | Action |
|---|---|---|
| Link | Enter | Activate |
| Button | Enter, Space | Activate |
| Checkbox | Space | Toggle |
| Radio group | Arrow Up/Down | Select previous/next |
| Tab list | Arrow Left/Right | Switch tab |
| Menu | Arrow Up/Down | Navigate items |
| Menu | Enter | Select item |
| Menu | Escape | Close menu |
| Dialog | Escape | Close dialog |
| Slider | Arrow Left/Right | Decrease/increase |
| Combobox | Arrow Down | Open dropdown |
| Combobox | Enter | Select highlighted |
| Combobox | Escape | Close dropdown |
| Tree | Arrow Up/Down | Navigate siblings |
| Tree | Arrow Right | Expand / enter child |
| Tree | Arrow Left | Collapse / go to parent |
Target Size Reference
| Standard | Minimum Size | Notes |
|---|---|---|
| WCAG 2.5.8 (AA) | 24x24px CSS pixels | Or adequate spacing between targets |
| WCAG 2.5.5 (AAA) | 44x44px CSS pixels | Recommended for touch interfaces |
| Apple HIG | 44x44pt | iOS touch targets |
| Material Design | 48x48dp | Android touch targets |
Exceptions: Inline links within text, browser default controls, essential fixed-size elements.
OKLCH Color Accessibility Standards
Reference guide for OKLCH-based perceptual color analysis. Used by color-auditor and fix-implementer roles.
OKLCH Basics
oklch(Lightness% Chroma Hue)- Lightness (L): 0% (black) to 100% (white) -- perceptually uniform
- Chroma (C): 0 (achromatic/gray) to ~0.37 (maximum saturation) -- perceptual colorfulness
- Hue (H): 0-360 degrees -- color wheel angle
OKLCH is perceptually uniform: equal numeric changes produce equal perceived changes, unlike HSL/RGB.
Lightness Guidelines
| Element | L Range | Rationale |
|---|---|---|
| Dark text on light background | L <= 40% | Ensures sufficient contrast against light surfaces |
| Light background for text | L >= 90% | Provides clean reading surface |
| Accent colors (interactive) | L 50-65% | Vibrant but readable |
| Disabled/muted elements | L 55-70% | Intentionally reduced contrast (with care) |
| Dark mode text | L >= 85% | Light text on dark background |
| Dark mode background | L <= 20% | Dark surface for light text |
Chroma Guidelines
| Use Case | C Range | Notes |
|---|---|---|
| Neutral/text colors | C = 0 | Pure achromatic for maximum readability |
| Subtle warm/cool tint | C = 0.005-0.02 | Adds warmth without color distraction |
| Standard accent | C = 0.1-0.15 | Good balance of color and readability |
| Vibrant accent | C = 0.2-0.25 | Use sparingly, max 1-2 per palette |
| Maximum saturation | C > 0.25 | Avoid for text; OK for decorative only |
Impeccable Palette Reference
/* Neutrals */
--color-ink: oklch(10% 0 0); /* Primary text */
--color-paper: oklch(98% 0 0); /* Primary background */
--color-cream: oklch(96% 0.005 350); /* Warm background variant */
--color-charcoal: oklch(25% 0 0); /* Secondary text */
--color-ash: oklch(55% 0 0); /* Muted/disabled text */
--color-mist: oklch(92% 0 0); /* Border/divider */
/* Accent */
--color-accent: oklch(60% 0.25 350); /* Primary action */
--color-accent-hover: oklch(52% 0.25 350); /* Hover state (darker) */Palette Analysis
| Name | L | C | H | Role | Contrast vs paper (98%) |
|---|---|---|---|---|---|
| ink | 10% | 0 | 0 | Body text | ~15.4:1 (AAA) |
| charcoal | 25% | 0 | 0 | Secondary text | ~9.5:1 (AAA) |
| ash | 55% | 0 | 0 | Muted text | ~3.8:1 (AA large only) |
| accent | 60% | 0.25 | 350 | Interactive | ~3.4:1 (large text / non-text only) |
| accent-hover | 52% | 0.25 | 350 | Hover state | ~4.6:1 (AA) |
Contrast Verification Methods
WCAG 2.1 Contrast Ratio
Formula: (L1 + 0.05) / (L2 + 0.05) where L1, L2 are relative luminances (L1 > L2).
Convert OKLCH -> sRGB -> linear RGB -> relative luminance: L_rel = 0.2126 * R_linear + 0.7152 * G_linear + 0.0722 * B_linear
| Text Type | AA | AAA |
|---|---|---|
| Normal text (< 18pt / < 14pt bold) | >= 4.5:1 | >= 7:1 |
| Large text (>= 18pt / >= 14pt bold) | >= 3:1 | >= 4.5:1 |
| Non-text (UI components, icons) | >= 3:1 | >= 3:1 |
APCA Contrast (Advanced)
APCA (Accessible Perceptual Contrast Algorithm) uses OKLCH lightness for perceptual accuracy.
Lc (Lightness contrast) values:
| Use Case | Minimum Lc | Description |
|---|---|---|
| Body text (14-16px) | >= 75 | Primary reading content |
| Content text (18px) | >= 60 | Secondary content |
| Large text (24px+) | >= 45 | Headings, display |
| Non-text elements | >= 30 | Icons, borders, focus indicators |
| Placeholder/disabled | >= 15 | Intentionally muted |
Focus Indicator Contrast
Focus indicators must have >= 3:1 contrast against adjacent colors:
- Outline color vs element background
- Outline color vs surrounding page background
- Both must pass
Color Blindness Reference
Confusion Lines
| Type | Prevalence | Confuses | Safe Alternatives |
|---|---|---|---|
| Protanopia (red-blind) | ~1% male | Red/green, red/brown | Use blue/orange, add patterns |
| Deuteranopia (green-blind) | ~5% male | Green/red, green/brown | Use blue/orange, add patterns |
| Tritanopia (blue-blind) | ~0.01% | Blue/yellow | Use red/green pairs (opposite) |
| Achromatopsia (no color) | ~0.003% | All chromatic | Rely on lightness difference only |
Safe Color Pairs
Always ensure information is not conveyed by color alone. Safe strategies:
- Use lightness difference (>= 40% L difference)
- Add text labels, icons, or patterns alongside color
- Use blue + orange (safe for most color blindness types)
- Avoid red-only or green-only indicators
Dark Mode Color Mapping
When converting light mode to dark mode:
| Light Mode | Dark Mode | Rule |
|---|---|---|
| Text L <= 40% | Text L >= 85% | Invert lightness range |
| Background L >= 90% | Background L <= 20% | Invert lightness range |
| Accent L 50-65% | Accent L 60-75% | Slight lightness increase |
| Border L ~92% | Border L ~25% | Invert proportionally |
Maintain same hue and similar chroma; adjust lightness to preserve contrast ratios.
Pipeline Definitions
Visual accessibility pipeline modes and task registry.
Pipeline Modes
| Mode | Description | Task Count |
|---|---|---|
| audit-only | 3 parallel auditors -> remediation plan | 4 tasks |
| full | 3 parallel auditors -> remediation -> fix -> 2 parallel re-auditors | 7 tasks |
Audit-Only Pipeline Task Registry
| Task ID | Role | blockedBy | Description |
|---|---|---|---|
| COLOR-001 | color-auditor | [] | OKLCH color contrast audit: WCAG 2.1 + APCA ratios, color blindness simulation |
| TYPO-001 | typo-auditor | [] | Typography readability: font size at breakpoints, line-height, reading width |
| FOCUS-001 | focus-auditor | [] | Focus management: tab order, indicators, skip link, ARIA, keyboard |
| REMED-001 | remediation-planner | [COLOR-001, TYPO-001, FOCUS-001] | Prioritized remediation plan with code-level fixes |
CRITICAL: COLOR-001, TYPO-001, FOCUS-001 have NO blockedBy -- they run in PARALLEL.
Full Pipeline Task Registry
| Task ID | Role | blockedBy | Description |
|---|---|---|---|
| COLOR-001 | color-auditor | [] | OKLCH color contrast audit (initial) |
| TYPO-001 | typo-auditor | [] | Typography readability audit |
| FOCUS-001 | focus-auditor | [] | Focus management audit (initial) |
| REMED-001 | remediation-planner | [COLOR-001, TYPO-001, FOCUS-001] | Prioritized remediation plan |
| FIX-001 | fix-implementer | [REMED-001] | Implement a11y fixes from remediation plan |
| COLOR-002 | color-auditor | [FIX-001] | Re-audit color contrast after fixes |
| FOCUS-002 | focus-auditor | [FIX-001] | Re-audit focus management after fixes |
CRITICAL: COLOR-002 and FOCUS-002 both blocked only by FIX-001 -- they run in PARALLEL.
Dependency Graphs
Audit-Only
COLOR-001 --+
|
TYPO-001 --+--> REMED-001
|
FOCUS-001 --+Full
COLOR-001 --+
|
TYPO-001 --+--> REMED-001 --> FIX-001 --+--> COLOR-002
| |
FOCUS-001 --+ +--> FOCUS-002Fan-In Points
| Point | Waiting For | Gate Task | Action |
|---|---|---|---|
| Audit fan-in | COLOR-001 + TYPO-001 + FOCUS-001 (all 3) | REMED-001 | Unblock REMED-001 when all 3 complete |
| Re-audit fan-in | COLOR-002 + FOCUS-002 (both) | Pipeline complete | Check GC convergence |
GC Loop Behavior (Full Mode)
After re-audit fan-in (COLOR-002 + FOCUS-002 both complete):
| Signal | Condition | Action |
|---|---|---|
| All pass | 0 critical + 0 high issues remaining | GC converged -> pipeline complete |
| Issues remain | Critical or high issues found | gc_rounds < 2 -> create FIX-002 + re-audit tasks |
| Max rounds | gc_rounds >= 2 | Escalate to user: accept / try one more / terminate |
Parallel Spawn Rules
| Mode | Scenario | Spawn Behavior |
|---|---|---|
| audit-only | Initial | Spawn COLOR-001 + TYPO-001 + FOCUS-001 in parallel |
| audit-only | After 3 audits | Spawn REMED-001 |
| full | Initial | Spawn COLOR-001 + TYPO-001 + FOCUS-001 in parallel |
| full | After 3 audits | Spawn REMED-001 |
| full | After REMED-001 | Spawn FIX-001 |
| full | After FIX-001 | Spawn COLOR-002 + FOCUS-002 in parallel |
| full (GC) | After re-audit fan-in | If issues: spawn FIX-002, then new re-audits |
Output Artifacts
| Task | Output Path |
|---|---|
| COLOR-001 | <session>/audits/color/color-audit-001.md |
| TYPO-001 | <session>/audits/typography/typo-audit-001.md |
| FOCUS-001 | <session>/audits/focus/focus-audit-001.md |
| REMED-001 | <session>/remediation/remediation-plan.md |
| FIX-001 | Modified source files + <session>/fixes/fix-summary-001.md |
| COLOR-002 | <session>/re-audit/color-audit-002.md |
| FOCUS-002 | <session>/re-audit/focus-audit-002.md |
{
"team_name": "visual-a11y",
"team_display_name": "Visual Accessibility",
"description": "Visual accessibility QA team with parallel auditors, remediation planning, and fix implementation with re-audit verification",
"version": "1.0.0",
"roles": {
"coordinator": {
"task_prefix": null,
"responsibility": "Scope assessment (page/component/full site), pipeline orchestration, parallel fan-in coordination",
"message_types": ["task_unblocked", "fan_in_complete", "gc_check", "error", "shutdown"]
},
"color-auditor": {
"task_prefix": "COLOR",
"responsibility": "OKLCH lightness/chroma analysis, contrast ratio (WCAG 2.1 + APCA), color blindness simulation",
"message_types": ["audit_complete", "audit_progress", "error"]
},
"typo-auditor": {
"task_prefix": "TYPO",
"responsibility": "Typography readability: font size at breakpoints, line-height ratios, clamp() validation, reading width",
"message_types": ["audit_complete", "audit_progress", "error"]
},
"focus-auditor": {
"task_prefix": "FOCUS",
"responsibility": "Focus-visible completeness: tab-order, focus indicator visibility, skip-link, focus traps, ARIA live regions",
"message_types": ["audit_complete", "audit_progress", "error"]
},
"remediation-planner": {
"task_prefix": "REMED",
"responsibility": "Synthesize audit findings into prioritized remediation plan with code-level fix guidance",
"message_types": ["plan_ready", "plan_progress", "error"]
},
"fix-implementer": {
"task_prefix": "FIX",
"inner_loop": true,
"responsibility": "Implement a11y fixes: OKLCH color corrections, focus styles, ARIA attributes, reduced-motion queries",
"message_types": ["fix_complete", "fix_progress", "error"]
}
},
"pipelines": {
"audit-only": {
"description": "3 parallel auditors -> remediation plan",
"task_chain": [
"COLOR-001 || TYPO-001 || FOCUS-001",
"REMED-001"
],
"fan_in_points": ["REMED-001"],
"complexity": "medium"
},
"full": {
"description": "3 parallel auditors -> remediation -> fix -> 2 parallel re-auditors",
"task_chain": [
"COLOR-001 || TYPO-001 || FOCUS-001",
"REMED-001",
"FIX-001",
"COLOR-002 || FOCUS-002"
],
"fan_in_points": ["REMED-001"],
"gc_points": ["COLOR-002", "FOCUS-002"],
"complexity": "high"
}
},
"innovation_patterns": {
"parallel_fan_in": {
"pattern": "CP-3",
"description": "3 auditors run simultaneously, remediation waits for all 3",
"fan_in_count": 3,
"fan_in_roles": ["color-auditor", "typo-auditor", "focus-auditor"],
"gate_task": "REMED-001"
},
"generator_critic": {
"pattern": "CP-2",
"generator": "fix-implementer",
"critic": ["color-auditor", "focus-auditor"],
"max_rounds": 2,
"convergence": "re-audit finds 0 critical and 0 high issues",
"escalation": "Coordinator intervenes after max rounds"
},
"shared_memory": {
"file": ".msg/meta.json",
"fields": {
"color-auditor": ["total_combinations", "pass_count", "fail_count", "critical_count"],
"typo-auditor": ["total_rules", "pass_count", "fail_count", "breakpoints_tested"],
"focus-auditor": ["total_elements", "pass_count", "fail_count", "skip_link_present"],
"remediation-planner": ["total_issues", "critical", "high", "medium", "low"],
"fix-implementer": ["total_fixes", "critical_fixed", "high_fixed", "files_modified"]
}
},
"chrome_devtools": {
"description": "Chrome DevTools integration for rendered verification",
"tools": ["evaluate_script", "take_screenshot", "navigate_page", "resize_page", "emulate", "lighthouse_audit"],
"degradation": "If unavailable, degrade to static analysis only"
}
},
"session_dirs": {
"base": ".workflow/.team/VA-{slug}-{YYYY-MM-DD}/",
"color_audit": "audits/color/",
"typo_audit": "audits/typography/",
"focus_audit": "audits/focus/",
"remediation": "remediation/",
"fixes": "fixes/",
"re_audit": "re-audit/",
"evidence": "evidence/",
"messages": ".workflow/.team-msg/{team-name}/"
}
}
Typography Accessibility Standards
Reference guide for typography readability analysis. Used by typo-auditor and fix-implementer roles.
Font Stack Hierarchy
| Purpose | Category | Example | Usage |
|---|---|---|---|
| Display | Serif | Cormorant Garamond, Georgia, serif | Headings, hero text |
| Body | Sans-serif | Instrument Sans, Inter, system-ui, sans-serif | Paragraphs, UI text |
| Mono | Monospace | Space Grotesk, Fira Code, monospace | Code blocks, labels |
System Font Stack (Fallback)
--font-body: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-mono: ui-monospace, "Cascadia Code", "Fira Code", Menlo, monospace;Responsive Size Scale
clamp() Formula Pattern
/* clamp(minimum, preferred, maximum) */
/* preferred = slope * 100vw + intercept */
/* slope = (max - min) / (max-viewport - min-viewport) */Recommended Scale
| Element | clamp() Value | Min (320px) | Max (1400px) | WCAG |
|---|---|---|---|---|
| Hero | clamp(2.5rem, 7vw, 4.5rem) | 40px | 72px | -- |
| Section title | clamp(1.75rem, 4vw, 2.5rem) | 28px | 40px | -- |
| H1 | clamp(2rem, 5vw, 3rem) | 32px | 48px | -- |
| H2 | clamp(1.5rem, 3vw, 2.25rem) | 24px | 36px | -- |
| H3 | clamp(1.25rem, 2vw, 1.75rem) | 20px | 28px | -- |
| Body | clamp(1rem, 1vw + 0.875rem, 1.25rem) | 16px | 20px | 1.4.4 |
| Small/caption | 14px (fixed floor) | 14px | 14px | 1.4.4 |
Absolute Minimums
| Element | Minimum | Rationale |
|---|---|---|
| Body text | 16px | WCAG 1.4.4 baseline readability |
| Small text / caption | 14px | Absolute floor for legibility |
| Interactive labels | 14px | Touch/click target readability |
Never: Set body text below 14px at any viewport width.
Line Height Scale
| Element Type | Range | Optimal | Rationale |
|---|---|---|---|
| Body text | 1.5 - 1.75 | 1.625 | WCAG 1.4.12, optimal readability |
| Headings | 1.1 - 1.3 | 1.2 | Tight for visual impact, still readable |
| Code blocks | 1.5 - 1.7 | 1.6 | Generous for scanning |
| Buttons/labels | 1.2 - 1.5 | 1.3 | Compact UI element |
| Lists | 1.4 - 1.6 | 1.5 | Slightly tighter than body |
Line Height Rules
- Use unitless values (not px or em) for proper inheritance
line-height: 1.5(unitless) is preferred overline-height: 24px(fixed)- WCAG 1.4.12 requires text to remain readable at 1.5x font-size line-height
Letter Spacing Scale
| Element Type | Range | Optimal | Notes |
|---|---|---|---|
| Display headings | -0.02em to 0 | -0.015em | Tight for visual weight |
| Body text | 0 (default) | 0 | Browser default is optimal |
| Uppercase labels | 0.05em - 0.1em | 0.08em | Open for legibility |
| Monospace code | 0.08em - 0.15em | 0.1em | Wide for character distinction |
| Small caps | 0.05em - 0.08em | 0.06em | Slight opening |
WCAG 1.4.12 Override Requirements
Content must remain readable when user applies:
- Letter spacing: 0.12em
- Word spacing: 0.16em
- These overrides should not cause content overflow or overlap
Reading Width
| Metric | Optimal | Acceptable | Flag |
|---|---|---|---|
| Characters per line | 66ch | 45-75ch | < 45 (too narrow) or > 75 (too wide) |
| Container max-width | 65ch | 60ch-75ch | > 80ch |
| Content column width | Up to 900px | Up to 1000px | > 1000px without columns |
Implementation
/* Preferred: ch-based */
.content { max-width: 70ch; }
/* Alternative: px-based */
.content { max-width: 700px; }
/* With centering */
.content {
max-width: 70ch;
margin-inline: auto;
padding-inline: 1rem;
}Font Loading Strategy
font-display Values
| Value | Behavior | Recommended For |
|---|---|---|
swap | Show fallback immediately, swap when loaded | Body text |
optional | Show fallback, swap only if already cached | Display/decorative fonts |
fallback | Brief invisible period (~100ms), then fallback | Important fonts |
block | Invisible text up to 3s | Avoid for body text |
auto | Browser decides | Avoid (unpredictable) |
Best Practices
/* Body font: swap for immediate readability */
@font-face {
font-family: "Body Font";
src: url("body.woff2") format("woff2");
font-display: swap;
}
/* Display font: optional to prevent layout shift */
@font-face {
font-family: "Display Font";
src: url("display.woff2") format("woff2");
font-display: optional;
}Preloading Critical Fonts
<link rel="preload" href="/fonts/body.woff2" as="font" type="font/woff2" crossorigin>Fallback Stack Requirements
- Every custom font must have a system fallback
- Fallback should have similar metrics (x-height, width) to minimize layout shift
- Use
size-adjust,ascent-override,descent-overridefor metric matching
Responsive Typography Checklist
| # | Check | Standard | Method |
|---|---|---|---|
| 1 | Body text >= 16px at 320px | WCAG 1.4.4 | Computed style at mobile viewport |
| 2 | No text below 14px at any viewport | Best practice | Search for font-size values |
| 3 | clamp() min >= 14px | Best practice | Parse clamp() functions |
| 4 | Line-height unitless | Best practice | Search for px/em line-height |
| 5 | Body line-height 1.5-1.75 | WCAG 1.4.12 | Computed style check |
| 6 | Reading width 45-75ch | Best practice | Measure container + font size |
| 7 | font-display: swap on body | Best practice | Parse @font-face rules |
| 8 | System fallback defined | Best practice | Parse font-family stacks |
| 9 | 200% zoom no overflow | WCAG 1.4.4 | Zoom test |
| 10 | Text spacing override safe | WCAG 1.4.12 | Apply overrides, check layout |
WCAG 2.1 Criteria Matrix
Reference matrix for visual accessibility audit. Maps WCAG success criteria to audit methods and responsible roles.
Criteria Covered by This Team
| Criterion | Level | Automated | Manual | Check Method | Primary Role |
|---|---|---|---|---|---|
| 1.4.3 Contrast (Minimum) | AA | Yes | - | Color ratio calculation (4.5:1 normal, 3:1 large) | color-auditor |
| 1.4.6 Contrast (Enhanced) | AAA | Yes | - | Color ratio calculation (7:1 normal, 4.5:1 large) | color-auditor |
| 1.4.11 Non-text Contrast | AA | Partial | Yes | UI component border/fill contrast >= 3:1 | color-auditor |
| 1.4.4 Resize Text | AA | Yes | - | 200% zoom, no content loss | typo-auditor |
| 1.4.12 Text Spacing | AA | Partial | Yes | Override line-height 1.5x, letter-spacing 0.12em, word-spacing 0.16em | typo-auditor |
| 2.1.1 Keyboard | A | Partial | Yes | Tab through all interactive elements | focus-auditor |
| 2.1.2 No Keyboard Trap | A | - | Yes | Tab can escape all contexts (except intentional traps with Escape) | focus-auditor |
| 2.4.1 Bypass Blocks | A | Yes | - | Skip link present and functional | focus-auditor |
| 2.4.3 Focus Order | A | - | Yes | Logical tab sequence | focus-auditor |
| 2.4.7 Focus Visible | AA | Partial | Yes | Visible focus indicator on all interactive elements | focus-auditor |
| 2.4.11 Focus Not Obscured (Min) | AA | - | Yes | Focus indicator not hidden by overlays, sticky headers | focus-auditor |
| 2.5.8 Target Size (Minimum) | AA | Yes | - | Minimum 24x24px CSS pixel touch targets | focus-auditor |
| 4.1.2 Name, Role, Value | A | Yes | - | ARIA roles, labels, and states on interactive elements | focus-auditor |
| 4.1.3 Status Messages | AA | Partial | Yes | aria-live regions for dynamic status updates | focus-auditor |
Audit Method Legend
| Method | Description |
|---|---|
| Automated | Can be verified programmatically (CSS parsing, contrast calculation) |
| Manual | Requires human judgment or runtime interaction |
| Partial | Automated detection of presence, manual verification of correctness |
Criterion Details
1.4.3 Contrast (Minimum) - AA
Requirement: Text and images of text have a contrast ratio of at least 4.5:1 (3:1 for large text).
Large text definition: >= 18pt (24px) or >= 14pt bold (18.66px bold).
Exceptions: Decorative text, logos, inactive UI components.
Check: Extract foreground/background colors -> calculate contrast ratio -> compare against threshold.
1.4.6 Contrast (Enhanced) - AAA
Requirement: Text has contrast ratio of at least 7:1 (4.5:1 for large text).
Check: Same as 1.4.3 but with higher thresholds.
1.4.11 Non-text Contrast - AA
Requirement: UI components and graphical objects have at least 3:1 contrast against adjacent colors.
Applies to: Buttons, inputs, icons, focus indicators, charts, custom controls.
Check: Extract border/fill colors of UI components -> calculate contrast against background.
1.4.4 Resize Text - AA
Requirement: Text can be resized up to 200% without loss of content or functionality.
Check: Zoom to 200% -> verify no horizontal scrolling, no content clipping, no overlap.
1.4.12 Text Spacing - AA
Requirement: Content must remain readable when user overrides:
- Line height to 1.5x font size
- Paragraph spacing to 2x font size
- Letter spacing to 0.12x font size
- Word spacing to 0.16x font size
Check: Apply overrides via JavaScript/CSS -> verify no content loss or overlap.
2.1.1 Keyboard - A
Requirement: All functionality available via keyboard (Enter, Space, Tab, Arrow keys, Escape).
Check: Tab to every interactive element -> activate with Enter/Space -> navigate composites with Arrows.
2.1.2 No Keyboard Trap - A
Requirement: Keyboard focus can be moved away from any component using standard keys.
Exception: Modal dialogs that trap focus intentionally (must have Escape to exit).
Check: Tab forward and backward through all elements -> verify escape from all containers.
2.4.1 Bypass Blocks - A
Requirement: Mechanism to bypass repeated content blocks (skip links, landmarks, headings).
Check: Verify skip link as first focusable element, target exists, focus moves correctly.
2.4.3 Focus Order - A
Requirement: Focus order preserves meaning and operability (logical sequence).
Check: Tab through page -> verify order matches visual/reading order.
2.4.7 Focus Visible - AA
Requirement: Keyboard focus indicator is visible on all interactive elements.
Check: Tab to each element -> verify visible outline/border/shadow change.
2.4.11 Focus Not Obscured (Minimum) - AA
Requirement: Focused element is not entirely hidden by other content (sticky headers, overlays, banners).
Check: Tab through elements near sticky/fixed positioned elements -> verify focus indicator partially visible.
2.5.8 Target Size (Minimum) - AA
Requirement: Interactive targets are at least 24x24 CSS pixels, or have adequate spacing.
Exceptions: Inline links within text, user agent default controls, essential presentation.
Check: Measure interactive element dimensions -> verify >= 24x24px or adequate spacing.
4.1.2 Name, Role, Value - A
Requirement: All UI components have accessible name, role, and state programmatically determined.
Check: Verify interactive elements have:
- Accessible name (text content, aria-label, aria-labelledby)
- Appropriate role (native HTML or ARIA role)
- Current state (aria-expanded, aria-pressed, aria-selected, aria-checked)
4.1.3 Status Messages - AA
Requirement: Status messages can be programmatically determined through role or properties without receiving focus.
Check: Verify dynamic content updates use aria-live or appropriate roles (status, alert, log).