picasso-skill 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -56,7 +56,7 @@ Upload `skills/picasso/SKILL.md` as a Custom Skill in Claude.ai settings. Upload
56
56
 
57
57
  | | Skill | Agent |
58
58
  |---|---|---|
59
- | **What it is** | Static knowledge base (13 reference files) | Autonomous design engineer |
59
+ | **What it is** | Static knowledge base (20 reference files) | Autonomous design engineer |
60
60
  | **How it works** | Loaded into context when designing | Runs as a sub-process with its own tools |
61
61
  | **When it runs** | When you ask for design help | Proactively after any frontend code change |
62
62
  | **Can audit code** | Only when you ask | Automatically |
@@ -71,22 +71,32 @@ Upload `skills/picasso/SKILL.md` as a Custom Skill in Claude.ai settings. Upload
71
71
  skills/picasso/
72
72
  SKILL.md # Main skill file (knowledge base)
73
73
  references/
74
- typography.md # Type systems, font pairing, scales, OpenType
75
- color-and-contrast.md # OKLCH, tinted neutrals, dark mode, a11y
74
+ anti-patterns.md # AI slop fingerprint + what NOT to do
75
+ typography.md # Font pairing, type scales, variable fonts, 12 curated pairings
76
+ color-and-contrast.md # OKLCH, 10 curated palettes, P3 wide gamut, dark mode
76
77
  spatial-design.md # Spacing scales, grids, visual hierarchy
77
78
  motion-and-animation.md # Easing, staggering, text morphing, reduced motion
78
79
  interaction-design.md # Forms, focus, loading, empty states, errors
79
- responsive-design.md # Mobile-first, fluid, container queries, dvh/svh
80
+ responsive-design.md # Mobile-first, dvh/svh, container queries, print
80
81
  sensory-design.md # UI sounds, haptic feedback, multi-sensory
81
82
  react-patterns.md # React 19, Tailwind v4, Server Actions, dark mode
82
- anti-patterns.md # What NOT to do (the most important file)
83
+ accessibility-wcag.md # Full ARIA patterns, WCAG 2.2, SPA focus management
84
+ modern-css-performance.md # :has(), scroll animations, view transitions, @layer
85
+ performance-optimization.md # Core Web Vitals, 45 React/Next.js perf patterns
86
+ ux-writing.md # Button labels, error templates, microcopy, banned words
87
+ ux-psychology.md # Gestalt, cognitive laws, scanning patterns
88
+ conversion-design.md # Landing pages, CTAs, pricing, onboarding
89
+ data-visualization.md # Charts, dashboards, data palettes, Tufte principles
90
+ style-presets.md # 22 curated visual presets with colors + fonts
83
91
  design-system.md # DESIGN.md generation, theming, tokens (OKLCH)
84
92
  generative-art.md # p5.js, SVG, Canvas 2D, noise, seeded randomness
85
93
  component-patterns.md # Standard naming, taxonomy, state matrix
86
- accessibility.md # ARIA, keyboard nav, screen readers, WCAG 2.2
87
94
 
88
95
  agents/
89
96
  picasso.md # Autonomous design auditor agent
97
+
98
+ templates/
99
+ picasso-config.md # .picasso.md project config template
90
100
  ```
91
101
 
92
102
  ## Agent Commands
@@ -107,7 +117,13 @@ The Picasso agent responds to these commands:
107
117
  | `/theme` | Generate or apply a theme |
108
118
  | `/stitch` | Generate a complete DESIGN.md from the current codebase |
109
119
  | `/harden` | Add error handling, loading states, empty states |
110
- | `/a11y` | Accessibility-only audit (axe-core, ARIA, contrast, keyboard) |
120
+ | `/a11y` | Accessibility audit: axe-core + pa11y + Lighthouse |
121
+ | `/perf` | Lighthouse performance audit with Core Web Vitals thresholds |
122
+ | `/visual-diff` | Screenshot desktop + mobile in light/dark, analyze visually |
123
+ | `/consistency` | Multi-page consistency check across all routes |
124
+ | `/lint-design` | Find hardcoded colors, spacing, fonts, z-index chaos |
125
+ | `/install-hooks` | Generate git pre-commit hook for design checks |
126
+ | `/ci-setup` | Generate GitHub Actions workflow for PR design review |
111
127
 
112
128
  ## Agent Audit Checklist
113
129
 
package/agents/picasso.md CHANGED
@@ -41,7 +41,13 @@ Before judging anything, understand what you're working with.
41
41
  1. **Identify changed files** -- run `git diff --name-only` and `git diff --staged --name-only` to find modified frontend files (.tsx, .jsx, .css, .html, .svelte, .vue, .astro)
42
42
  2. **Read the files** -- read every changed frontend file in full. Do not review code you haven't read.
43
43
  3. **Find the design system** -- search for `DESIGN.md`, `tailwind.config.*`, `theme.ts`, `tokens.css`, `globals.css`, or CSS variable definitions. If a design system exists, all findings must be measured against it.
44
- 4. **Check for existing patterns** -- grep for common component imports (shadcn, radix, headless-ui, chakra, mantine) to understand the component library in use.
44
+ 4. **Load project design config** -- search for `.picasso.md` in the project root (or locate it with `glob **/.picasso.md`). If found, parse it and treat its values as the project's declared design preferences:
45
+ - **Typography overrides** -- if the config declares a font (e.g., Inter, Roboto), do NOT flag it as AI-slop. The project has intentionally chosen it.
46
+ - **Color overrides** -- if the config declares a primary accent or neutral tint, validate usage against those values instead of Picasso defaults.
47
+ - **Design settings** -- honor `DESIGN_VARIANCE`, `MOTION_INTENSITY`, and `VISUAL_DENSITY` when calibrating the severity and scope of suggestions.
48
+ - **Constraints** -- treat every listed constraint as a hard requirement that overrides other Picasso recommendations (e.g., if "No animations" is listed, skip all motion suggestions).
49
+ - If `.picasso.md` is **not found**, proceed with Picasso defaults and note in the report that no project config was detected. You can generate one with the config template at `templates/picasso-config.md`.
50
+ 5. **Check for existing patterns** -- grep for common component imports (shadcn, radix, headless-ui, chakra, mantine) to understand the component library in use.
45
51
 
46
52
  ## Phase 2: Design Audit
47
53
 
@@ -310,7 +316,304 @@ When the user invokes these commands, execute the corresponding workflow:
310
316
  | `/theme` | Generate or apply a theme via DESIGN.md |
311
317
  | `/stitch` | Generate a complete DESIGN.md from the current codebase |
312
318
  | `/harden` | Add error handling, loading states, empty states, edge case handling |
313
- | `/a11y` | Accessibility-only audit: run axe-core, check ARIA, validate contrast, test keyboard nav |
319
+ | `/a11y` | Accessibility-only audit: run axe-cli, pa11y, and Lighthouse accessibility category with JSON output parsing; check ARIA, validate contrast, test keyboard nav |
320
+ | `/perf` | Performance audit: run Lighthouse CLI, extract Core Web Vitals (LCP, CLS, INP/TBT), report with pass/fail thresholds |
321
+ | `/visual-diff` | Visual regression: take desktop + mobile screenshots in light and dark mode, analyze for AI-slop indicators |
322
+ | `/consistency` | Multi-page consistency check: discover routes, run checks across all pages, produce cross-page comparison table |
323
+ | `/lint-design` | Design token linting: find hardcoded colors, inconsistent spacing, non-standard fonts, z-index chaos, transition:all |
324
+ | `/install-hooks` | Generate a git pre-commit hook that runs fast grep-based design checks (no server needed) |
325
+ | `/ci-setup` | Generate a GitHub Actions workflow for PR design review: a11y, perf, screenshots, PR comment |
326
+
327
+ ## Advanced Automation Commands
328
+
329
+ ### /perf -- Performance Audit
330
+
331
+ Run Lighthouse CLI, extract Core Web Vitals (LCP, CLS, INP/TBT), report scores with pass/fail thresholds:
332
+
333
+ ```bash
334
+ npx lighthouse http://localhost:3000 --only-categories=performance --output=json --output-path=/tmp/lh-perf.json --chrome-flags="--headless --no-sandbox" --quiet
335
+ ```
336
+
337
+ Parse the JSON output to extract these metrics with thresholds:
338
+
339
+ | Metric | Pass | Needs Work | Fail |
340
+ |---|---|---|---|
341
+ | Performance Score | >= 90 | 50-89 | < 50 |
342
+ | FCP (First Contentful Paint) | < 1.8s | 1.8-3.0s | > 3.0s |
343
+ | LCP (Largest Contentful Paint) | < 2.5s | 2.5-4.0s | > 4.0s |
344
+ | CLS (Cumulative Layout Shift) | < 0.1 | 0.1-0.25 | > 0.25 |
345
+ | TBT (Total Blocking Time) | < 200ms | 200-600ms | > 600ms |
346
+ | SI (Speed Index) | < 3.4s | 3.4-5.8s | > 5.8s |
347
+
348
+ ```bash
349
+ # Parse results from JSON
350
+ node -e "
351
+ const r = require('/tmp/lh-perf.json');
352
+ const a = r.audits;
353
+ console.log('Performance Score:', Math.round(r.categories.performance.score * 100));
354
+ console.log('FCP:', a['first-contentful-paint'].displayValue);
355
+ console.log('LCP:', a['largest-contentful-paint'].displayValue);
356
+ console.log('CLS:', a['cumulative-layout-shift'].displayValue);
357
+ console.log('TBT:', a['total-blocking-time'].displayValue);
358
+ console.log('SI:', a['speed-index'].displayValue);
359
+ "
360
+ ```
361
+
362
+ ### /visual-diff -- Visual Regression
363
+
364
+ Take screenshots at desktop (1440x900) and mobile (375x812), both light and dark mode. Use Playwright screenshot commands:
365
+
366
+ ```bash
367
+ # Desktop - Light mode
368
+ npx playwright screenshot http://localhost:3000 /tmp/picasso-desktop-light.png --viewport-size=1440,900 2>/dev/null
369
+
370
+ # Desktop - Dark mode (inject prefers-color-scheme)
371
+ npx playwright screenshot http://localhost:3000 /tmp/picasso-desktop-dark.png --viewport-size=1440,900 --color-scheme=dark 2>/dev/null
372
+
373
+ # Mobile - Light mode
374
+ npx playwright screenshot http://localhost:3000 /tmp/picasso-mobile-light.png --viewport-size=375,812 2>/dev/null
375
+
376
+ # Mobile - Dark mode
377
+ npx playwright screenshot http://localhost:3000 /tmp/picasso-mobile-dark.png --viewport-size=375,812 --color-scheme=dark 2>/dev/null
378
+ ```
379
+
380
+ Analyze all four screenshots visually for:
381
+ - AI-slop indicators (generic gradients, everything centered, uniform card grids)
382
+ - Light/dark mode consistency (same hierarchy, no lost contrast, no invisible elements)
383
+ - Mobile responsiveness (no overflow, readable text, adequate touch targets)
384
+ - Visual regression from previous state (if baseline screenshots exist)
385
+
386
+ ### /consistency -- Multi-Page Consistency Check
387
+
388
+ Discover routes (from file-system routing or user input), run the same checks across all pages, produce a cross-page comparison table:
389
+
390
+ ```bash
391
+ # Discover routes from Next.js app directory
392
+ find src/app -name "page.tsx" -o -name "page.jsx" 2>/dev/null | sed 's|src/app||;s|/page\.\(tsx\|jsx\)||;s|^$|/|'
393
+
394
+ # Or from pages directory
395
+ find src/pages -name "*.tsx" -o -name "*.jsx" 2>/dev/null | sed 's|src/pages||;s|\.\(tsx\|jsx\)||;s|/index$|/|'
396
+ ```
397
+
398
+ For each discovered route:
399
+ 1. Take a screenshot
400
+ 2. Extract font families used (`grep -rn 'font-family\|fontFamily'`)
401
+ 3. Extract color values used
402
+ 4. Extract spacing patterns
403
+ 5. Check for shared component usage
404
+
405
+ Output a cross-page comparison table:
406
+
407
+ ```
408
+ | Page | Font Families | Primary Colors | Spacing Base | Shared Components |
409
+ |----------|---------------|----------------|--------------|-------------------|
410
+ | / | Geist, mono | oklch(...) | 4px scale | Header, Footer |
411
+ | /about | Geist, mono | oklch(...) | 4px scale | Header, Footer |
412
+ | /pricing | Geist, serif | #3b82f6 (!) | mixed (!) | Header only (!) |
413
+ ```
414
+
415
+ Flag inconsistencies with `(!)` markers.
416
+
417
+ ### /lint-design -- Design Token Linting
418
+
419
+ Run Stylelint + grep-based checks to find design system violations:
420
+
421
+ ```bash
422
+ # 1. Find hardcoded colors that should be tokens
423
+ grep -rn '#[0-9a-fA-F]\{3,8\}' --include="*.tsx" --include="*.jsx" --include="*.css" | grep -v 'node_modules\|\.git\|\.next' | head -30
424
+
425
+ # 2. Find inconsistent spacing values (non-4px-multiple)
426
+ grep -rn 'padding\|margin\|gap' --include="*.css" --include="*.tsx" | grep -oP '\d+px' | sort | uniq -c | sort -rn
427
+
428
+ # 3. Find non-standard font stacks
429
+ grep -rn 'font-family\|fontFamily' --include="*.css" --include="*.tsx" --include="*.jsx" | grep -v 'node_modules' | head -20
430
+
431
+ # 4. Find z-index chaos (values not from a defined scale)
432
+ grep -rn 'z-index\|zIndex' --include="*.css" --include="*.tsx" --include="*.jsx" | grep -v 'node_modules' | head -20
433
+
434
+ # 5. Find transition:all (anti-pattern)
435
+ grep -rn 'transition:\s*all\|transition-property:\s*all' --include="*.css" --include="*.tsx" --include="*.jsx" | grep -v 'node_modules'
436
+
437
+ # 6. Run Stylelint if available
438
+ npx stylelint "**/*.css" --formatter=json 2>/dev/null || true
439
+ ```
440
+
441
+ Report findings grouped by category with severity and suggested token replacements.
442
+
443
+ ### /install-hooks -- Git Pre-commit Hook
444
+
445
+ Generate a `.git/hooks/pre-commit` script that runs fast design checks (grep-based, no server needed):
446
+
447
+ ```bash
448
+ cat > .git/hooks/pre-commit << 'HOOK'
449
+ #!/usr/bin/env bash
450
+ set -e
451
+
452
+ STAGED=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(tsx|jsx|css|html|svelte|vue)$' || true)
453
+ [ -z "$STAGED" ] && exit 0
454
+
455
+ ERRORS=0
456
+
457
+ echo "Running Picasso pre-commit checks..."
458
+
459
+ # 1. transition:all detection
460
+ if echo "$STAGED" | xargs grep -l 'transition:\s*all' 2>/dev/null; then
461
+ echo "ERROR: transition:all found. Specify properties explicitly."
462
+ ERRORS=$((ERRORS + 1))
463
+ fi
464
+
465
+ # 2. Pure black (#000) detection
466
+ if echo "$STAGED" | xargs grep -l '#000000\|#000[^0-9a-fA-F]' 2>/dev/null; then
467
+ echo "ERROR: Pure black (#000) found. Use tinted near-black instead."
468
+ ERRORS=$((ERRORS + 1))
469
+ fi
470
+
471
+ # 3. outline:none detection (without focus-visible replacement)
472
+ if echo "$STAGED" | xargs grep -l 'outline:\s*none\|outline:\s*0[^.]' 2>/dev/null; then
473
+ echo "WARNING: outline:none found. Ensure :focus-visible has a replacement."
474
+ ERRORS=$((ERRORS + 1))
475
+ fi
476
+
477
+ # 4. Missing alt text detection
478
+ if echo "$STAGED" | xargs grep -l '<img' 2>/dev/null | xargs grep -L 'alt=' 2>/dev/null; then
479
+ echo "ERROR: <img> tags without alt attribute found."
480
+ ERRORS=$((ERRORS + 1))
481
+ fi
482
+
483
+ if [ "$ERRORS" -gt 0 ]; then
484
+ echo ""
485
+ echo "Picasso found $ERRORS design issue(s). Fix them before committing."
486
+ exit 1
487
+ fi
488
+
489
+ echo "Picasso pre-commit checks passed."
490
+ exit 0
491
+ HOOK
492
+ chmod +x .git/hooks/pre-commit
493
+ echo "Pre-commit hook installed."
494
+ ```
495
+
496
+ ### /ci-setup -- GitHub Actions Workflow
497
+
498
+ Generate a `.github/workflows/picasso-review.yml` that runs on PRs touching frontend files:
499
+
500
+ ```yaml
501
+ name: Picasso Design Review
502
+
503
+ on:
504
+ pull_request:
505
+ paths:
506
+ - '**/*.tsx'
507
+ - '**/*.jsx'
508
+ - '**/*.css'
509
+ - '**/*.html'
510
+ - '**/*.svelte'
511
+ - '**/*.vue'
512
+
513
+ jobs:
514
+ picasso-review:
515
+ runs-on: ubuntu-latest
516
+ steps:
517
+ - uses: actions/checkout@v4
518
+
519
+ - uses: actions/setup-node@v4
520
+ with:
521
+ node-version: '20'
522
+ cache: 'npm'
523
+
524
+ - run: npm ci
525
+
526
+ - name: Start dev server
527
+ run: npm run dev &
528
+ env:
529
+ PORT: 3000
530
+
531
+ - name: Wait for server
532
+ run: npx wait-on http://localhost:3000 --timeout 60000
533
+
534
+ - name: Accessibility audit (axe-cli)
535
+ run: npx axe-cli http://localhost:3000 --exit --save /tmp/axe-results.json || true
536
+
537
+ - name: Accessibility audit (pa11y)
538
+ run: npx pa11y http://localhost:3000 --reporter json > /tmp/pa11y-results.json || true
539
+
540
+ - name: Lighthouse accessibility
541
+ run: |
542
+ npx lighthouse http://localhost:3000 --only-categories=accessibility --output=json --output-path=/tmp/lh-a11y.json --chrome-flags="--headless --no-sandbox" --quiet || true
543
+
544
+ - name: Lighthouse performance
545
+ run: |
546
+ npx lighthouse http://localhost:3000 --only-categories=performance --output=json --output-path=/tmp/lh-perf.json --chrome-flags="--headless --no-sandbox" --quiet || true
547
+
548
+ - name: Take screenshots
549
+ run: |
550
+ npx playwright install chromium --with-deps
551
+ npx playwright screenshot http://localhost:3000 /tmp/picasso-desktop.png --viewport-size=1440,900
552
+ npx playwright screenshot http://localhost:3000 /tmp/picasso-mobile.png --viewport-size=375,812
553
+
554
+ - name: Parse scores
555
+ id: scores
556
+ run: |
557
+ PERF=$(node -e "const r=require('/tmp/lh-perf.json');console.log(Math.round(r.categories.performance.score*100))" 2>/dev/null || echo "N/A")
558
+ A11Y=$(node -e "const r=require('/tmp/lh-a11y.json');console.log(Math.round(r.categories.accessibility.score*100))" 2>/dev/null || echo "N/A")
559
+ echo "perf=$PERF" >> $GITHUB_OUTPUT
560
+ echo "a11y=$A11Y" >> $GITHUB_OUTPUT
561
+
562
+ - name: Upload artifacts
563
+ uses: actions/upload-artifact@v4
564
+ with:
565
+ name: picasso-results
566
+ path: /tmp/picasso-*.png
567
+
568
+ - name: Post PR comment
569
+ uses: actions/github-script@v7
570
+ with:
571
+ script: |
572
+ const perf = '${{ steps.scores.outputs.perf }}';
573
+ const a11y = '${{ steps.scores.outputs.a11y }}';
574
+ const body = `## Picasso Design Review\n\n| Metric | Score |\n|---|---|\n| Performance | ${perf}/100 |\n| Accessibility | ${a11y}/100 |\n\nScreenshots uploaded as workflow artifacts.`;
575
+ github.rest.issues.createComment({
576
+ issue_number: context.issue.number,
577
+ owner: context.repo.owner,
578
+ repo: context.repo.repo,
579
+ body
580
+ });
581
+ ```
582
+
583
+ ### /a11y -- Accessibility Audit (Enhanced)
584
+
585
+ Run all three accessibility tools with JSON output parsing:
586
+
587
+ ```bash
588
+ # 1. axe-cli -- WCAG 2.1 AA violations
589
+ npx axe-cli http://localhost:3000 --exit --save /tmp/axe-results.json 2>/dev/null
590
+ node -e "
591
+ const r = require('/tmp/axe-results.json');
592
+ const v = r[0]?.violations || [];
593
+ console.log('axe-cli: ' + v.length + ' violations');
594
+ v.forEach(v => console.log(' [' + v.impact + '] ' + v.id + ': ' + v.description + ' (' + v.nodes.length + ' nodes)'));
595
+ "
596
+
597
+ # 2. pa11y -- HTML_CodeSniffer + WCAG 2.1 AA
598
+ npx pa11y http://localhost:3000 --reporter json > /tmp/pa11y-results.json 2>/dev/null
599
+ node -e "
600
+ const r = require('/tmp/pa11y-results.json');
601
+ console.log('pa11y: ' + r.length + ' issues');
602
+ r.forEach(i => console.log(' [' + i.type + '] ' + i.code + ': ' + i.message));
603
+ "
604
+
605
+ # 3. Lighthouse accessibility category
606
+ npx lighthouse http://localhost:3000 --only-categories=accessibility --output=json --output-path=/tmp/lh-a11y.json --chrome-flags="--headless --no-sandbox" --quiet
607
+ node -e "
608
+ const r = require('/tmp/lh-a11y.json');
609
+ const score = Math.round(r.categories.accessibility.score * 100);
610
+ console.log('Lighthouse a11y score: ' + score + '/100');
611
+ const failed = Object.values(r.audits).filter(a => a.score === 0);
612
+ failed.forEach(a => console.log(' FAIL: ' + a.id + ' - ' + a.title));
613
+ "
614
+ ```
615
+
616
+ Combine results from all three tools, deduplicate overlapping findings, and report with severity levels.
314
617
 
315
618
  ## Rules
316
619
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picasso-skill",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "The ultimate AI design skill for producing distinctive, production-grade frontend interfaces",
5
5
  "bin": {
6
6
  "picasso-skill": "./bin/install.mjs"
@@ -0,0 +1,245 @@
1
+ # Accessibility & WCAG 2.2 Reference
2
+
3
+ ## 1. ARIA Patterns Catalog
4
+
5
+ ### Dialog / Modal
6
+ - `role="dialog"`, `aria-modal="true"`, `aria-labelledby` (title)
7
+ - Tab/Shift+Tab cycle within dialog (focus trap), Escape closes
8
+ - On open: focus first focusable element. On close: return focus to trigger.
9
+
10
+ ```html
11
+ <div role="dialog" aria-modal="true" aria-labelledby="dlg-title">
12
+ <h2 id="dlg-title">Confirm Delete</h2>
13
+ <p>This action cannot be undone.</p>
14
+ <button>Cancel</button>
15
+ <button>Delete</button>
16
+ </div>
17
+ ```
18
+
19
+ ### Tabs
20
+ - `tablist` > `tab` + `tabpanel`
21
+ - `aria-selected="true|false"` on each tab, `aria-controls` linking tab to panel
22
+ - Left/Right arrows between tabs, Home/End to first/last
23
+
24
+ ### Accordion
25
+ - Heading elements containing `<button>` triggers
26
+ - `aria-expanded="true|false"` on button, `aria-controls` pointing to content
27
+ - Enter/Space toggles section
28
+
29
+ ### Combobox / Autocomplete
30
+ - `role="combobox"` on input, popup uses `listbox`
31
+ - `aria-expanded`, `aria-controls`, `aria-activedescendant`, `aria-autocomplete`
32
+ - Down Arrow opens/navigates popup, Escape closes, Enter selects
33
+
34
+ ### Menu / Menubar
35
+ - `menu`/`menubar` > `menuitem`, `menuitemcheckbox`, `menuitemradio`
36
+ - `aria-haspopup`, `aria-expanded` on submenu triggers
37
+ - Arrow keys navigate, Enter/Space activates, Escape closes submenu
38
+
39
+ ### Listbox
40
+ - `listbox` > `option`
41
+ - `aria-selected`, `aria-multiselectable` for multi-select
42
+ - Up/Down arrows, Home/End, type-ahead character navigation
43
+
44
+ ### Tree View
45
+ - `tree` > `treeitem` (nested groups use `group` role)
46
+ - `aria-expanded` on parent nodes, `aria-level`, `aria-setsize`, `aria-posinset`
47
+ - Right expands/enters child, Left collapses/moves to parent
48
+
49
+ ### Toolbar
50
+ - `toolbar` on container, `aria-orientation`
51
+ - Arrow keys between controls (roving tabindex), Tab moves out entirely
52
+
53
+ ### Feed
54
+ - `feed` > `article` on each entry
55
+ - `aria-busy` while loading, `aria-setsize`/`aria-posinset` on articles
56
+ - Page Down/Up between articles
57
+
58
+ ### Alert / Alert Dialog
59
+ - `role="alert"` (non-modal): implicitly `aria-live="assertive"`. No focus change.
60
+ - `role="alertdialog"` (modal): follows dialog focus trap pattern.
61
+
62
+ ### Breadcrumb
63
+ - `<nav aria-label="Breadcrumb">` with ordered list
64
+ - `aria-current="page"` on current page link
65
+
66
+ ### Disclosure
67
+ - `<button aria-expanded="false" aria-controls="content-id">` toggles content
68
+ - Enter/Space toggles expansion
69
+
70
+ ---
71
+
72
+ ## 2. Focus Management for SPAs
73
+
74
+ ### Route Change Announcements
75
+ ```html
76
+ <div aria-live="polite" class="sr-only" id="route-announcer"></div>
77
+ ```
78
+ Update textContent on route change: "Products page loaded".
79
+
80
+ ### Focus Restoration
81
+ Move focus to `<h1>` of new view (add `tabindex="-1"`) or main content landmark. On modal close, restore focus to trigger element.
82
+
83
+ ### Skip Links
84
+ ```html
85
+ <a href="#main" class="skip-link">Skip to main content</a>
86
+ <main id="main" tabindex="-1">...</main>
87
+ ```
88
+
89
+ ### Focus Trapping
90
+ Contain Tab/Shift+Tab within overlays. Use `inert` attribute on background content (modern browsers) or manage via JS. On last focusable element, Tab wraps to first.
91
+
92
+ ### Roving Tabindex
93
+ Only one child has `tabindex="0"` at a time; all others `tabindex="-1"`. On arrow key, swap values and `.focus()`. Alternative: `aria-activedescendant` on container.
94
+
95
+ ---
96
+
97
+ ## 3. Accessible Forms
98
+
99
+ ### Error Handling
100
+ ```html
101
+ <input id="email" aria-invalid="true" aria-describedby="email-err" aria-required="true">
102
+ <span id="email-err" role="alert">Please enter a valid email address.</span>
103
+ ```
104
+
105
+ - `aria-invalid="true"` on fields with errors.
106
+ - `aria-describedby` linking to error message.
107
+ - `role="alert"` for immediate screen reader announcement.
108
+ - On submit, move focus to first invalid field.
109
+
110
+ ### Required Fields
111
+ Use both `required` (native) and `aria-required="true"`. Pair with visible asterisk + legend.
112
+
113
+ ### Field Descriptions
114
+ ```html
115
+ <label for="pw">Password</label>
116
+ <input id="pw" type="password" aria-describedby="pw-hint">
117
+ <p id="pw-hint">Must be at least 8 characters with one number.</p>
118
+ ```
119
+
120
+ ### Autocomplete Attributes
121
+ Use `autocomplete` values: `name`, `email`, `tel`, `street-address`, `postal-code`, `cc-number`, etc.
122
+
123
+ ### Group Labeling
124
+ ```html
125
+ <fieldset>
126
+ <legend>Payment Method</legend>
127
+ <label><input type="radio" name="pay" value="card"> Credit Card</label>
128
+ <label><input type="radio" name="pay" value="paypal"> PayPal</label>
129
+ </fieldset>
130
+ ```
131
+
132
+ ### Accessible Date Pickers
133
+ Prefer native `<input type="date">`. For custom: `role="grid"` calendar, arrow key navigation, Enter to select, Escape to close, label each cell with full date.
134
+
135
+ ---
136
+
137
+ ## 4. WCAG 2.2 New Criteria
138
+
139
+ ### Target Size Minimum (2.5.8 -- Level AA)
140
+ All interactive targets must be at least **24x24 CSS pixels**.
141
+
142
+ ```css
143
+ button, a, input, select, [role="button"] {
144
+ min-width: 24px;
145
+ min-height: 24px;
146
+ }
147
+ ```
148
+
149
+ ### Dragging Alternatives (2.5.7 -- Level AA)
150
+ Every drag-and-drop operation must have a single-pointer alternative (click/tap). Sortable lists must also support "Move Up"/"Move Down" buttons.
151
+
152
+ ### Focus Appearance (2.4.11 -- Level AA)
153
+ Focus indicators: minimum **2px perimeter outline** with **3:1 contrast ratio** between focused and unfocused states.
154
+
155
+ ```css
156
+ :focus-visible {
157
+ outline: 2px solid #005fcc;
158
+ outline-offset: 2px;
159
+ }
160
+ ```
161
+
162
+ ### Consistent Help (3.2.6 -- Level A)
163
+ Help mechanisms must appear in the **same relative order** across all pages.
164
+
165
+ ---
166
+
167
+ ## 5. Screen Reader Dynamic Content
168
+
169
+ ### aria-live Regions
170
+ ```html
171
+ <div aria-live="polite" aria-atomic="true">3 results found</div>
172
+ <div aria-live="assertive">Session expiring in 30 seconds</div>
173
+ ```
174
+ - `polite`: announced after current speech.
175
+ - `assertive`: interrupts (use sparingly).
176
+ - `aria-atomic="true"`: reads entire region, not just changed text.
177
+
178
+ ### Status Messages (WCAG 4.1.3)
179
+ ```html
180
+ <div role="status">File uploaded successfully.</div>
181
+ ```
182
+
183
+ ### Loading States
184
+ ```html
185
+ <div role="status" aria-live="polite">Loading results...</div>
186
+ <table aria-busy="true">...</table>
187
+ ```
188
+ Set `aria-busy="true"` during update, `false` when complete.
189
+
190
+ ### Progress Updates
191
+ ```html
192
+ <div role="progressbar" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100"
193
+ aria-label="Upload progress">65%</div>
194
+ ```
195
+
196
+ ---
197
+
198
+ ## 6. Accessible Data Tables
199
+
200
+ ### Complex Headers
201
+ ```html
202
+ <th id="q1" scope="col">Q1</th>
203
+ <th id="revenue" scope="row">Revenue</th>
204
+ <td headers="q1 revenue">$1.2M</td>
205
+ ```
206
+
207
+ ### Sortable Columns
208
+ ```html
209
+ <th aria-sort="ascending" scope="col">
210
+ <button>Name <span aria-hidden="true">&uarr;</span></button>
211
+ </th>
212
+ ```
213
+ Values: `ascending`, `descending`, `none`. Only one column sorted at a time.
214
+
215
+ ### Expandable Rows
216
+ Parent row: `aria-expanded="true|false"`. Use `aria-level`, `aria-setsize`, `aria-posinset` for treegrid.
217
+
218
+ ### Responsive Tables
219
+ - **Reflow:** Transform cells into stacked blocks with `data-label` + CSS `::before` for headers.
220
+ - **Scroll:** `tabindex="0"`, `role="region"`, `aria-label="Scrollable table"`.
221
+
222
+ ---
223
+
224
+ ## 7. Accessible Drag and Drop
225
+
226
+ ### Keyboard Alternatives (WCAG 2.5.7)
227
+ ```html
228
+ <li aria-roledescription="sortable item">
229
+ Item A
230
+ <button aria-label="Move Item A up">Up</button>
231
+ <button aria-label="Move Item A down">Down</button>
232
+ </li>
233
+ ```
234
+
235
+ ### Live Region Announcements
236
+ ```html
237
+ <div aria-live="assertive" class="sr-only" id="dnd-status"></div>
238
+ ```
239
+ - On grab: "Grabbed Item A. Current position 2 of 5."
240
+ - On move: "Item A moved to position 3 of 5."
241
+ - On drop: "Item A dropped at position 3 of 5."
242
+ - On cancel: "Reorder cancelled. Item A returned to position 2."
243
+
244
+ ### Modern Pattern
245
+ `aria-grabbed`/`aria-dropeffect` are deprecated. Use `aria-roledescription="draggable"`, `aria-pressed`/`aria-selected` for state, and live regions for announcements. Space/Enter to grab/drop, arrow keys to reposition, Escape to cancel.