@softspark/ai-toolkit 2.4.1 → 2.5.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/AGENTS.md +32 -19
- package/CHANGELOG.md +23 -0
- package/README.md +11 -11
- package/app/.claude-plugin/plugin.json +1 -1
- package/app/agents/code-reviewer.md +6 -7
- package/app/agents/frontend-specialist.md +33 -2
- package/app/agents/seo-specialist.md +1 -1
- package/app/personas/frontend-lead.md +48 -5
- package/app/skills/a11y-validate/SKILL.md +377 -0
- package/app/skills/a11y-validate/reference/aria-patterns.md +259 -0
- package/app/skills/a11y-validate/reference/eaa-compliance.md +252 -0
- package/app/skills/a11y-validate/reference/mobile-eaa.md +329 -0
- package/app/skills/a11y-validate/reference/wcag-2-1-aa.md +285 -0
- package/app/skills/a11y-validate/reference/wcag-2-2-aa.md +221 -0
- package/app/skills/a11y-validate/scripts/a11y-scanner.py +639 -0
- package/app/skills/clean-code/reference/python.md +3 -3
- package/app/skills/design-engineering/SKILL.md +2 -5
- package/app/skills/review/SKILL.md +30 -6
- package/app/skills/seo-validate/SKILL.md +460 -0
- package/app/skills/seo-validate/reference/core-web-vitals.md +445 -0
- package/app/skills/seo-validate/reference/geo-aeo-patterns.md +259 -0
- package/app/skills/seo-validate/reference/geo-guidelines.md +248 -0
- package/app/skills/seo-validate/reference/schema-types.md +465 -0
- package/app/skills/seo-validate/reference/spa-ssg-patterns.md +351 -0
- package/app/skills/seo-validate/reference/w3c-guidelines.md +289 -0
- package/app/skills/seo-validate/scripts/seo-scanner.py +549 -0
- package/bin/ai-toolkit.js +4 -4
- package/kb/reference/architecture-overview.md +1 -1
- package/kb/reference/comparison.md +1 -1
- package/kb/reference/skills-catalog.md +3 -1
- package/llms-full.txt +10 -6
- package/manifest.json +3 -3
- package/package.json +2 -2
- package/scripts/config_cli.py +4 -10
- package/scripts/doctor.py +3 -3
- package/scripts/install_steps/ai_tools.py +1 -1
- package/scripts/plugin.py +1 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# WCAG 2.1 Level A + AA Reference
|
|
2
|
+
|
|
3
|
+
Reference for `a11y-validate`. All 50 WCAG 2.1 Level A + AA success criteria, with a detection-pattern matrix (which are statically detectable vs. runtime-only).
|
|
4
|
+
|
|
5
|
+
Source: W3C Web Content Accessibility Guidelines (WCAG) 2.1 Recommendation, 5 June 2018 (most recent maintenance: October 2023).
|
|
6
|
+
|
|
7
|
+
## Legend
|
|
8
|
+
|
|
9
|
+
- **Static** — detectable via source-code pattern matching (this skill's wheelhouse).
|
|
10
|
+
- **Heuristic** — static detection possible but with false-positive/negative risk.
|
|
11
|
+
- **Runtime** — requires rendering, interaction, or AT testing; beyond static analysis.
|
|
12
|
+
- **Authoring** — depends on content choices, not code patterns.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Principle 1: Perceivable
|
|
17
|
+
|
|
18
|
+
### Guideline 1.1 — Text Alternatives
|
|
19
|
+
|
|
20
|
+
#### 1.1.1 Non-text Content (Level A) — **Static**
|
|
21
|
+
Images, form inputs, and non-text UI components must have text alternatives. Detection: `<img>` without `alt`, icon fonts without `aria-label`, `<svg>` without `<title>`/`aria-label`, form inputs without label, decorative images with non-empty alt.
|
|
22
|
+
|
|
23
|
+
### Guideline 1.2 — Time-based Media
|
|
24
|
+
|
|
25
|
+
#### 1.2.1 Audio-only and Video-only (Prerecorded) (Level A) — **Static**
|
|
26
|
+
Detection: `<audio>` without transcript; `<video>` without audio track requires captions.
|
|
27
|
+
|
|
28
|
+
#### 1.2.2 Captions (Prerecorded) (Level A) — **Static**
|
|
29
|
+
Detection: `<video>` without `<track kind="captions">`. **EAA legal risk.**
|
|
30
|
+
|
|
31
|
+
#### 1.2.3 Audio Description or Media Alternative (Prerecorded) (Level A) — **Static**
|
|
32
|
+
Detection: `<video>` without `<track kind="descriptions">` and no linked transcript.
|
|
33
|
+
|
|
34
|
+
#### 1.2.4 Captions (Live) (Level AA) — **Runtime**
|
|
35
|
+
Live caption quality can't be verified statically. Flag live-media patterns (WebRTC, YouTube Live embeds) for manual review.
|
|
36
|
+
|
|
37
|
+
#### 1.2.5 Audio Description (Prerecorded) (Level AA) — **Static**
|
|
38
|
+
Detection: Same as 1.2.3 but at AA level.
|
|
39
|
+
|
|
40
|
+
### Guideline 1.3 — Adaptable
|
|
41
|
+
|
|
42
|
+
#### 1.3.1 Info and Relationships (Level A) — **Static**
|
|
43
|
+
Detection: `<div>` used where semantic element appropriate; heading skip; missing landmarks; tables without `<th>`/`<caption>`.
|
|
44
|
+
|
|
45
|
+
#### 1.3.2 Meaningful Sequence (Level A) — **Heuristic**
|
|
46
|
+
CSS `order` / `flex-direction: row-reverse` that disconnects visual from DOM order. Hard to verify without rendering.
|
|
47
|
+
|
|
48
|
+
#### 1.3.3 Sensory Characteristics (Level A) — **Authoring**
|
|
49
|
+
Instructions like "click the button on the right" — content-driven.
|
|
50
|
+
|
|
51
|
+
#### 1.3.4 Orientation (Level AA) — **Static**
|
|
52
|
+
Detection: CSS `@media (orientation: portrait)` / `landscape` with content visibility rules that lock orientation.
|
|
53
|
+
|
|
54
|
+
#### 1.3.5 Identify Input Purpose (Level AA) — **Static**
|
|
55
|
+
Detection: `<input type="email/tel/name/cc-*/bday-*/street-*">` without `autocomplete` matching WCAG input-purpose taxonomy.
|
|
56
|
+
|
|
57
|
+
### Guideline 1.4 — Distinguishable
|
|
58
|
+
|
|
59
|
+
#### 1.4.1 Use of Color (Level A) — **Heuristic**
|
|
60
|
+
Detection: color-only signalling (links distinguished only by `color`, errors only by red, etc.).
|
|
61
|
+
|
|
62
|
+
#### 1.4.2 Audio Control (Level A) — **Static**
|
|
63
|
+
Detection: `<audio autoplay>` or `<video autoplay>` without `muted` AND without pause control.
|
|
64
|
+
|
|
65
|
+
#### 1.4.3 Contrast (Minimum) (Level AA) — **Heuristic**
|
|
66
|
+
Detection: hardcoded color pairs in CSS with computed contrast <4.5:1 (normal) or <3:1 (large text, 18pt+/14pt bold+). Runtime cascade may change this — flag as heuristic.
|
|
67
|
+
|
|
68
|
+
#### 1.4.4 Resize Text (Level AA) — **Static**
|
|
69
|
+
Detection: `<meta name="viewport">` with `user-scalable=no` or `maximum-scale=1`. Also flag `font-size` in `px` where rem/em would adapt better (INFO only).
|
|
70
|
+
|
|
71
|
+
#### 1.4.5 Images of Text (Level AA) — **Heuristic**
|
|
72
|
+
Detection: `<img>` with `alt` containing a full sentence — likely text rendered as image.
|
|
73
|
+
|
|
74
|
+
#### 1.4.10 Reflow (Level AA) — **Runtime**
|
|
75
|
+
Content must reflow at 320 CSS pixels width / 256 CSS pixels height without horizontal scroll. Requires rendering.
|
|
76
|
+
|
|
77
|
+
#### 1.4.11 Non-text Contrast (Level AA) — **Heuristic**
|
|
78
|
+
Detection: button/input border colors, focus indicator colors vs. adjacent colors — hardcoded combinations with <3:1 contrast.
|
|
79
|
+
|
|
80
|
+
#### 1.4.12 Text Spacing (Level AA) — **Runtime**
|
|
81
|
+
Content must adapt when users override line-height, letter-spacing, word-spacing, paragraph-spacing. Requires rendering.
|
|
82
|
+
|
|
83
|
+
#### 1.4.13 Content on Hover or Focus (Level AA) — **Runtime**
|
|
84
|
+
Tooltips/popovers must be dismissible, hoverable, persistent. Requires interaction testing.
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Principle 2: Operable
|
|
89
|
+
|
|
90
|
+
### Guideline 2.1 — Keyboard Accessible
|
|
91
|
+
|
|
92
|
+
#### 2.1.1 Keyboard (Level A) — **Heuristic**
|
|
93
|
+
Detection: `onClick` on `<div>`/`<span>` without role+tabindex+keydown; native `<button>` converted via CSS to link (acceptable) but custom `<a role="button">` without tabindex/key handlers (not acceptable).
|
|
94
|
+
|
|
95
|
+
#### 2.1.2 No Keyboard Trap (Level A) — **Heuristic**
|
|
96
|
+
Detection: `event.preventDefault()` in keydown within modal/dialog without Escape handling.
|
|
97
|
+
|
|
98
|
+
#### 2.1.4 Character Key Shortcuts (Level A) — **Heuristic**
|
|
99
|
+
Detection: global keydown listener on single-character keys without modifier. Should be disableable or only active on focus.
|
|
100
|
+
|
|
101
|
+
### Guideline 2.2 — Enough Time
|
|
102
|
+
|
|
103
|
+
#### 2.2.1 Timing Adjustable (Level A) — **Runtime**
|
|
104
|
+
Session timeouts, auto-refresh — requires behavioral testing.
|
|
105
|
+
|
|
106
|
+
#### 2.2.2 Pause, Stop, Hide (Level A) — **Static**
|
|
107
|
+
Detection: `<marquee>` / `<blink>` (deprecated); CSS `animation: ... infinite` on non-interactive element without pause control; auto-sliding carousel without pause button.
|
|
108
|
+
|
|
109
|
+
### Guideline 2.3 — Seizures and Physical Reactions
|
|
110
|
+
|
|
111
|
+
#### 2.3.1 Three Flashes or Below Threshold (Level A) — **Runtime**
|
|
112
|
+
Requires visual analysis.
|
|
113
|
+
|
|
114
|
+
### Guideline 2.4 — Navigable
|
|
115
|
+
|
|
116
|
+
#### 2.4.1 Bypass Blocks (Level A) — **Static**
|
|
117
|
+
Detection: No skip link (`<a href="#main">`) and no landmark `<main>`.
|
|
118
|
+
|
|
119
|
+
#### 2.4.2 Page Titled (Level A) — **Static**
|
|
120
|
+
Detection: `<title>` missing, empty, or duplicated across routes.
|
|
121
|
+
|
|
122
|
+
#### 2.4.3 Focus Order (Level A) — **Static**
|
|
123
|
+
Detection: `tabindex` value >0.
|
|
124
|
+
|
|
125
|
+
#### 2.4.4 Link Purpose (In Context) (Level A) — **Heuristic**
|
|
126
|
+
Detection: link text "click here", "read more", "here" without surrounding descriptive context.
|
|
127
|
+
|
|
128
|
+
#### 2.4.5 Multiple Ways (Level AA) — **Static**
|
|
129
|
+
Detection: Sitemap / search / navigation absence across project.
|
|
130
|
+
|
|
131
|
+
#### 2.4.6 Headings and Labels (Level AA) — **Static**
|
|
132
|
+
Detection: Empty `<h1>`, empty `<label>`, generic "Input" labels.
|
|
133
|
+
|
|
134
|
+
#### 2.4.7 Focus Visible (Level AA) — **Static**
|
|
135
|
+
Detection: `outline: none` / `outline: 0` on focusable selector without `:focus-visible` replacement.
|
|
136
|
+
|
|
137
|
+
### Guideline 2.5 — Input Modalities
|
|
138
|
+
|
|
139
|
+
#### 2.5.1 Pointer Gestures (Level A) — **Heuristic**
|
|
140
|
+
Detection: Multi-touch / path-based gestures (swipe, pinch) without single-pointer alternative.
|
|
141
|
+
|
|
142
|
+
#### 2.5.2 Pointer Cancellation (Level A) — **Heuristic**
|
|
143
|
+
Detection: `onMouseDown` triggering action without `onMouseUp` confirmation (up-event cancellation).
|
|
144
|
+
|
|
145
|
+
#### 2.5.3 Label in Name (Level A) — **Heuristic**
|
|
146
|
+
Detection: `<button aria-label="Close">✕</button>` where visible text ("Close") doesn't appear in accessible name. Also: button visible text "Submit" with `aria-label="Save"` — mismatched.
|
|
147
|
+
|
|
148
|
+
#### 2.5.4 Motion Actuation (Level A) — **Heuristic**
|
|
149
|
+
Detection: `devicemotion` / `deviceorientation` handlers without alternative UI control.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## Principle 3: Understandable
|
|
154
|
+
|
|
155
|
+
### Guideline 3.1 — Readable
|
|
156
|
+
|
|
157
|
+
#### 3.1.1 Language of Page (Level A) — **Static**
|
|
158
|
+
Detection: `<html>` missing `lang` attribute.
|
|
159
|
+
|
|
160
|
+
#### 3.1.2 Language of Parts (Level AA) — **Heuristic**
|
|
161
|
+
Detection: Foreign-language content without `<span lang="...">` wrapper.
|
|
162
|
+
|
|
163
|
+
### Guideline 3.2 — Predictable
|
|
164
|
+
|
|
165
|
+
#### 3.2.1 On Focus (Level A) — **Heuristic**
|
|
166
|
+
Detection: `onFocus` handler that changes context (navigation, submit, modal open).
|
|
167
|
+
|
|
168
|
+
#### 3.2.2 On Input (Level A) — **Heuristic**
|
|
169
|
+
Detection: `<form>` submitted on `onChange` of single input; `window.location` changed in `onChange` handler.
|
|
170
|
+
|
|
171
|
+
#### 3.2.3 Consistent Navigation (Level AA) — **Heuristic**
|
|
172
|
+
Detection: Navigation component with route-dependent render (different nav on different pages).
|
|
173
|
+
|
|
174
|
+
#### 3.2.4 Consistent Identification (Level AA) — **Heuristic**
|
|
175
|
+
Detection: Same UI element with different ARIA labels / icons across pages.
|
|
176
|
+
|
|
177
|
+
### Guideline 3.3 — Input Assistance
|
|
178
|
+
|
|
179
|
+
#### 3.3.1 Error Identification (Level A) — **Heuristic**
|
|
180
|
+
Detection: Error display without `aria-describedby` link from input.
|
|
181
|
+
|
|
182
|
+
#### 3.3.2 Labels or Instructions (Level A) — **Static**
|
|
183
|
+
Detection: `<input>` without label.
|
|
184
|
+
|
|
185
|
+
#### 3.3.3 Error Suggestion (Level AA) — **Authoring**
|
|
186
|
+
Content-driven: does the error message tell the user what's wrong and how to fix it?
|
|
187
|
+
|
|
188
|
+
#### 3.3.4 Error Prevention (Legal, Financial, Data) (Level AA) — **Heuristic**
|
|
189
|
+
Detection: Payment / delete / purchase form without confirmation step / review-before-submit.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Principle 4: Robust
|
|
194
|
+
|
|
195
|
+
### Guideline 4.1 — Compatible
|
|
196
|
+
|
|
197
|
+
#### 4.1.1 Parsing (Level A, removed in WCAG 2.2) — **Static**
|
|
198
|
+
Detection: Malformed HTML, duplicate IDs, unclosed tags. Modern browsers tolerate most parse errors; WCAG 2.2 removes this criterion.
|
|
199
|
+
|
|
200
|
+
#### 4.1.2 Name, Role, Value (Level A) — **Static**
|
|
201
|
+
Detection: `<div onClick>` without role; custom widgets without ARIA state; `aria-labelledby` pointing to non-existent ID.
|
|
202
|
+
|
|
203
|
+
#### 4.1.3 Status Messages (Level AA) — **Heuristic**
|
|
204
|
+
Detection: Toast / async error display without `role="status"` / `role="alert"` / `aria-live`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## WCAG 2.2 AA Additions (enable with `--standard wcag-2.2-aa`)
|
|
209
|
+
|
|
210
|
+
### 2.4.11 Focus Not Obscured (Minimum) (Level AA) — **Runtime**
|
|
211
|
+
Focused element must not be entirely hidden by author-created content (sticky headers, cookie banners).
|
|
212
|
+
|
|
213
|
+
### 2.5.7 Dragging Movements (Level AA) — **Heuristic**
|
|
214
|
+
Detection: drag-and-drop UI without keyboard alternative (buttons to reorder).
|
|
215
|
+
|
|
216
|
+
### 2.5.8 Target Size (Minimum) (Level AA) — **Heuristic**
|
|
217
|
+
Detection: interactive target <24×24 CSS pixels in source CSS.
|
|
218
|
+
|
|
219
|
+
### 3.2.6 Consistent Help (Level A) — **Heuristic**
|
|
220
|
+
Detection: Help / contact / support link not consistently placed across pages.
|
|
221
|
+
|
|
222
|
+
### 3.3.7 Redundant Entry (Level A) — **Heuristic**
|
|
223
|
+
Detection: Multi-step form asking for same data (email, name, address) twice without autofill.
|
|
224
|
+
|
|
225
|
+
### 3.3.8 Accessible Authentication (Minimum) (Level AA) — **Heuristic**
|
|
226
|
+
Detection: CAPTCHAs (reCAPTCHA v2 checkbox, image-based CAPTCHAs) without non-cognitive alternative; passwords without `autocomplete="current-password"` / `autocomplete="new-password"`.
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## EN 301 549 Additional Requirements (enable with `--standard en-301-549` or `eaa`)
|
|
231
|
+
|
|
232
|
+
EN 301 549 v3.2.1 aligns with WCAG 2.1 AA but adds:
|
|
233
|
+
|
|
234
|
+
- **Chapter 5: Generic requirements** — user preferences, biometric alternatives, privacy of personal information.
|
|
235
|
+
- **Chapter 6: ICT with two-way voice communication** — real-time text, captions for calls.
|
|
236
|
+
- **Chapter 7: ICT with video capabilities** — captions for recorded video.
|
|
237
|
+
- **Chapter 9: Web** — full WCAG 2.1 AA.
|
|
238
|
+
- **Chapter 10: Non-web documents** — WCAG for PDFs.
|
|
239
|
+
- **Chapter 11: Software** — WCAG-equivalent for native apps. **Critical for EAA mobile scope.**
|
|
240
|
+
- **Chapter 12: Documentation and support services** — accessibility information published.
|
|
241
|
+
- **Chapter 13: ICT providing relay / emergency services**.
|
|
242
|
+
|
|
243
|
+
### Chapter 11.x (Software / Mobile)
|
|
244
|
+
|
|
245
|
+
Corresponds to WCAG criteria applied to mobile/native apps:
|
|
246
|
+
|
|
247
|
+
| Clause | Maps to WCAG | Description |
|
|
248
|
+
|--------|--------------|-------------|
|
|
249
|
+
| 11.1.1.1 | 1.1.1 | Non-text content in software |
|
|
250
|
+
| 11.1.3.1 | 1.3.1 | Info and relationships in software |
|
|
251
|
+
| 11.2.1.1 | 2.1.1 | Keyboard alternatives in software |
|
|
252
|
+
| 11.4.1.1 | 4.1.1 | Parsing in software (native UI tree correctness) |
|
|
253
|
+
| 11.4.1.2 | 4.1.2 | Name, role, value in software |
|
|
254
|
+
|
|
255
|
+
For React Native / Flutter, this means: every interactive widget needs an accessible name, role, state exposed to the platform accessibility API (iOS UIAccessibility / Android AccessibilityNodeInfo).
|
|
256
|
+
|
|
257
|
+
See [mobile-eaa.md](mobile-eaa.md) for implementation patterns.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Detection Coverage Summary
|
|
262
|
+
|
|
263
|
+
Of 50 WCAG 2.1 Level A + AA criteria:
|
|
264
|
+
- **~20 statically detectable** (this skill's definitive findings)
|
|
265
|
+
- **~15 heuristically detectable** (confidence: heuristic)
|
|
266
|
+
- **~10 runtime-only** (needs rendering/interaction)
|
|
267
|
+
- **~5 authoring-only** (content decisions, not code)
|
|
268
|
+
|
|
269
|
+
For complete coverage, pair `/a11y-validate` with:
|
|
270
|
+
- **axe-core** (via Playwright / Cypress / Jest)
|
|
271
|
+
- **Lighthouse accessibility audit**
|
|
272
|
+
- **pa11y CLI**
|
|
273
|
+
- **NVDA / JAWS / VoiceOver / TalkBack manual testing**
|
|
274
|
+
- **User research with disabled participants**
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## References
|
|
279
|
+
|
|
280
|
+
- WCAG 2.1: https://www.w3.org/TR/WCAG21/
|
|
281
|
+
- WCAG 2.2: https://www.w3.org/TR/WCAG22/
|
|
282
|
+
- EN 301 549 v3.2.1: https://www.etsi.org/deliver/etsi_en/301500_301599/301549/03.02.01_60/en_301549v030201p.pdf
|
|
283
|
+
- WCAG Quick Reference: https://www.w3.org/WAI/WCAG21/quickref/
|
|
284
|
+
- Understanding WCAG: https://www.w3.org/WAI/WCAG21/Understanding/
|
|
285
|
+
- Techniques for WCAG: https://www.w3.org/WAI/WCAG21/Techniques/
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# WCAG 2.2 New Success Criteria Reference
|
|
2
|
+
|
|
3
|
+
Reference for `a11y-validate --standard wcag-2.2-aa`. Covers ONLY the 9 new success criteria added in WCAG 2.2 (not in 2.1). For the 50 inherited criteria, see [wcag-2-1-aa.md](wcag-2-1-aa.md).
|
|
4
|
+
|
|
5
|
+
Source: W3C WCAG 2.2 Recommendation, 5 October 2023.
|
|
6
|
+
|
|
7
|
+
Note: WCAG 2.2 removes SC 4.1.1 Parsing (Level A) -- modern parsers handle malformed markup; the criterion is obsolete.
|
|
8
|
+
|
|
9
|
+
## Legend
|
|
10
|
+
|
|
11
|
+
- **Heuristic** -- static detection possible with false-positive/negative risk.
|
|
12
|
+
- **Runtime** -- requires rendering, interaction, or AT testing.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Level A
|
|
17
|
+
|
|
18
|
+
### 2.4.11 Focus Not Obscured (Minimum) -- **Runtime**
|
|
19
|
+
|
|
20
|
+
When a component receives keyboard focus, it must not be entirely hidden by author-created content (sticky headers, cookie banners, chat widgets).
|
|
21
|
+
|
|
22
|
+
**Failures:** `position: fixed/sticky` elements with high `z-index` covering focused items. Cookie banners without `scroll-into-view` logic. Chat widgets overlapping tab-order elements.
|
|
23
|
+
|
|
24
|
+
```html
|
|
25
|
+
<!-- FAIL: focused link hidden behind sticky header -->
|
|
26
|
+
<header style="position: sticky; top: 0; z-index: 1000; height: 80px;">...</header>
|
|
27
|
+
<main><a href="/about">About</a></main>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Grep:** `position:\s*(fixed|sticky)` | `z-index:\s*[0-9]{3,}`
|
|
31
|
+
|
|
32
|
+
**Frameworks:** React/Next -- add `scroll-padding-top` on `<html>` to offset sticky headers. Vue/Nuxt -- same for `<Teleport>` portals. Angular -- verify `CDK Overlay` does not obscure focus.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
### 3.2.6 Consistent Help -- **Heuristic**
|
|
37
|
+
|
|
38
|
+
Help mechanisms (contact info, chat, FAQ) must appear in the same relative order across all pages in a set.
|
|
39
|
+
|
|
40
|
+
**Failures:** Help link in header on some pages, footer on others. Chat widget present on landing but absent on checkout.
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<!-- FAIL: Page A has help in header, Page B only in footer -->
|
|
44
|
+
<header><a href="/support">Help</a></header> <!-- Page A -->
|
|
45
|
+
<footer><a href="/support">Help</a></footer> <!-- Page B -->
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Grep:** `href=["'][^"']*(help|support|contact|faq)` | `(help|support|contact|chat|assistance)`
|
|
49
|
+
|
|
50
|
+
**Frameworks:** Place help links in shared layout -- `layout.tsx` (Next App Router), `layouts/default.vue` (Nuxt), shared `AppComponent` (Angular).
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### 3.3.7 Redundant Entry -- **Heuristic**
|
|
55
|
+
|
|
56
|
+
Data previously entered in the same process must be auto-populated or selectable. Exceptions: security re-entry, expired/deleted data.
|
|
57
|
+
|
|
58
|
+
**Failures:** Multi-step form re-asking email without pre-fill. Billing address blank with no "same as shipping" checkbox.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
// FAIL: Step 3 re-asks email without persisting from Step 1
|
|
62
|
+
function Step3() {
|
|
63
|
+
const [email, setEmail] = useState(''); // should pre-fill
|
|
64
|
+
return <input value={email} onChange={e => setEmail(e.target.value)} />;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Grep:** `(step|wizard|multi.?step|checkout)` | `(name|email|phone|address).*input`
|
|
69
|
+
|
|
70
|
+
**Frameworks:** React -- `useFormContext` (RHF) or Formik context across steps. Vue -- Pinia store. Angular -- shared `FormGroup` across stepper.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### 3.3.8 Accessible Authentication (Minimum) -- **Heuristic**
|
|
75
|
+
|
|
76
|
+
No cognitive function test for auth unless: (a) alternative exists, (b) mechanism helps complete it (paste, password manager), or (c) test uses object/personal-content recognition (allowed at Minimum, not Enhanced).
|
|
77
|
+
|
|
78
|
+
**Failures:** CAPTCHA with no non-cognitive alternative. Password field blocking paste or missing `autocomplete`. OTP input preventing paste.
|
|
79
|
+
|
|
80
|
+
```html
|
|
81
|
+
<!-- FAIL: blocks password manager -->
|
|
82
|
+
<input type="password" onpaste="return false">
|
|
83
|
+
<!-- FAIL: missing autocomplete -->
|
|
84
|
+
<input type="password" name="pwd">
|
|
85
|
+
<!-- PASS -->
|
|
86
|
+
<input type="password" name="pwd" autocomplete="current-password">
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Grep:** `onpaste.*preventDefault|onpaste.*return\s*false` | `captcha|recaptcha|hcaptcha` | `type=["']password["'](?!.*autocomplete)`
|
|
90
|
+
|
|
91
|
+
**Frameworks:** React uses `autoComplete="current-password"` (camelCase). Vue -- `autocomplete="current-password"`, no `@paste.prevent`. Angular -- `[attr.autocomplete]="'current-password'"`.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Level AA
|
|
96
|
+
|
|
97
|
+
### 2.4.12 Focus Not Obscured (Enhanced) -- **Runtime**
|
|
98
|
+
|
|
99
|
+
No part of a focused component may be hidden by author-created content. Stricter than 2.4.11 -- even partial overlap fails.
|
|
100
|
+
|
|
101
|
+
**Failures:** Same as 2.4.11 but partial coverage also fails. Sticky sidebar partially covering a focused input. Bottom nav overlapping lower portion of a button.
|
|
102
|
+
|
|
103
|
+
```css
|
|
104
|
+
/* Fix: offset fixed elements with scroll-padding */
|
|
105
|
+
html { scroll-padding-bottom: 80px; scroll-padding-top: 100px; }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Grep:** `position:\s*(fixed|sticky)` | `scroll-padding` | `scroll-margin`
|
|
109
|
+
|
|
110
|
+
**Frameworks:** All -- use `scroll-padding-top`/`scroll-padding-bottom` on scroll container. Tab through all interactive elements to verify.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### 2.4.13 Focus Appearance -- **Heuristic**
|
|
115
|
+
|
|
116
|
+
Focus indicator must have: (a) area >= perimeter x 2 CSS pixels (roughly a 2px outline), (b) >= 3:1 contrast between focused and unfocused states.
|
|
117
|
+
|
|
118
|
+
**Failures:** 1px dotted outline. Focus ring color close to background. `box-shadow`-only focus with low opacity.
|
|
119
|
+
|
|
120
|
+
```css
|
|
121
|
+
/* FAIL */ :focus-visible { outline: 1px dotted #ccc; }
|
|
122
|
+
/* FAIL */ :focus-visible { outline: 2px solid #e0e0e0; } /* ~1.1:1 on white */
|
|
123
|
+
/* PASS */ :focus-visible { outline: 2px solid #0056b3; outline-offset: 2px; }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Grep:** `:focus-visible|:focus\b` | `outline:\s*(1px|0\.5px|thin|dotted|none|0)` | `box-shadow.*:focus`
|
|
127
|
+
|
|
128
|
+
**Frameworks:** React -- `@react-aria/focus` provides compliant rings; Shadcn `ring-2 ring-ring` needs contrast audit. Vue -- audit Vuetify/PrimeVue focus styles against theme. Angular -- Material `FocusMonitor` handles detection but verify visual indicator. Tailwind -- `focus-visible:ring-2 focus-visible:ring-offset-2` with high-contrast color.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### 2.5.7 Dragging Movements -- **Heuristic**
|
|
133
|
+
|
|
134
|
+
Drag functionality must have a single-pointer non-drag alternative (unless dragging is essential).
|
|
135
|
+
|
|
136
|
+
**Failures:** Drag-only reorder list without move buttons. Custom slider without keyboard steps. Kanban with drag-only cards.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
// FAIL: drag-only, no button alternative
|
|
140
|
+
<DndContext onDragEnd={handleDragEnd}>
|
|
141
|
+
<SortableContext items={items}>
|
|
142
|
+
{items.map(item => <SortableItem key={item.id} {...item} />)}
|
|
143
|
+
</SortableContext>
|
|
144
|
+
</DndContext>
|
|
145
|
+
|
|
146
|
+
// PASS: add move buttons per item
|
|
147
|
+
<button onClick={() => moveUp(id)} aria-label="Move up">^</button>
|
|
148
|
+
<button onClick={() => moveDown(id)} aria-label="Move down">v</button>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Grep:** `draggable|onDrag|DndContext|useDrag|useDrop` | `react-beautiful-dnd|@dnd-kit|react-dnd|vuedraggable|cdkDrag|SortableJS`
|
|
152
|
+
|
|
153
|
+
**Frameworks:** React -- `@dnd-kit` supports `KeyboardSensor`; ensure it is registered. Vue -- `vuedraggable` lacks keyboard reorder; add move buttons. Angular -- `cdk/drag-drop` has keyboard support; verify active.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### 2.5.8 Target Size (Minimum) -- **Heuristic**
|
|
158
|
+
|
|
159
|
+
Interactive targets must be >= 24x24 CSS pixels, unless spacing compensates, target is inline, or size is essential.
|
|
160
|
+
|
|
161
|
+
**Failures:** 16x16 icon buttons without padding. Close (`x`) buttons with tiny tap targets. Dense table action icons.
|
|
162
|
+
|
|
163
|
+
```css
|
|
164
|
+
/* FAIL */ .icon-btn { width: 16px; height: 16px; padding: 0; }
|
|
165
|
+
/* PASS */ .icon-btn { width: 24px; height: 24px; }
|
|
166
|
+
/* PASS */ .icon-btn { width: 16px; height: 16px; padding: 4px; } /* 24x24 total */
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Grep:** `(width|height|min-width|min-height):\s*(1[0-9]|2[0-3])px` | `(w-[1-5]|h-[1-5])\b` | `padding:\s*0[;\s]`
|
|
170
|
+
|
|
171
|
+
**Frameworks:** React -- Shadcn `size="icon"` is 36x36 (safe); custom icons need `min-w-6 min-h-6`. Vue -- Vuetify `v-btn icon` is 40x40 (safe). Angular -- `mat-icon-button` is 40x40 (safe). Tailwind minimum: `min-w-6 min-h-6` (24px); prefer `min-w-11 min-h-11` (44px) for touch.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Level AAA
|
|
176
|
+
|
|
177
|
+
### 3.3.9 Accessible Authentication (Enhanced) -- **Heuristic**
|
|
178
|
+
|
|
179
|
+
Same as 3.3.8 but stricter: no cognitive test at all, including object recognition and personal content recognition. Only paste support, password manager autofill, or non-cognitive methods (WebAuthn, passkeys, magic links) qualify.
|
|
180
|
+
|
|
181
|
+
**Failures:** "Select all traffic lights" CAPTCHA (passes 3.3.8, fails 3.3.9). "Which is your profile photo?" (same). Any image/puzzle verification without fully non-cognitive alternative.
|
|
182
|
+
|
|
183
|
+
```html
|
|
184
|
+
<!-- FAIL at AAA --> <div class="g-recaptcha" data-sitekey="..."></div>
|
|
185
|
+
<!-- PASS --> <div class="g-recaptcha" data-sitekey="..." data-size="invisible"></div>
|
|
186
|
+
<!-- PASS --> <button onclick="navigator.credentials.get({publicKey: opts})">Passkey</button>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Grep:** `recaptcha|hcaptcha|captcha|turnstile` | `data-size=["']invisible["']` | `navigator\.credentials\.(get|create)` | `WebAuthn|passkey`
|
|
190
|
+
|
|
191
|
+
**Frameworks:** All -- prefer WebAuthn/passkeys, magic links, or invisible CAPTCHA (reCAPTCHA v3, Turnstile managed). At AAA, even "select all buses" fails.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Detection Coverage Summary
|
|
196
|
+
|
|
197
|
+
| Criterion | Level | Detection |
|
|
198
|
+
|-----------|-------|-----------|
|
|
199
|
+
| 2.4.11 Focus Not Obscured (Min) | A | Runtime |
|
|
200
|
+
| 2.4.12 Focus Not Obscured (Enhanced) | AA | Runtime |
|
|
201
|
+
| 2.4.13 Focus Appearance | AA | Heuristic |
|
|
202
|
+
| 2.5.7 Dragging Movements | AA | Heuristic |
|
|
203
|
+
| 2.5.8 Target Size (Minimum) | AA | Heuristic |
|
|
204
|
+
| 3.2.6 Consistent Help | A | Heuristic |
|
|
205
|
+
| 3.3.7 Redundant Entry | A | Heuristic |
|
|
206
|
+
| 3.3.8 Accessible Auth (Min) | AA | Heuristic |
|
|
207
|
+
| 3.3.9 Accessible Auth (Enhanced) | AAA | Heuristic |
|
|
208
|
+
|
|
209
|
+
- **0 statically detectable** -- no criterion is definitively matchable without context
|
|
210
|
+
- **7 heuristically detectable** -- the skill can flag likely violations
|
|
211
|
+
- **2 runtime-only** -- focus obscuring requires rendering to verify
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## References
|
|
216
|
+
|
|
217
|
+
- WCAG 2.2 Recommendation: https://www.w3.org/TR/WCAG22/
|
|
218
|
+
- What's New in WCAG 2.2: https://www.w3.org/WAI/standards-guidelines/wcag/new-in-22/
|
|
219
|
+
- Understanding WCAG 2.2: https://www.w3.org/WAI/WCAG22/Understanding/
|
|
220
|
+
- WCAG 2.2 Quick Reference: https://www.w3.org/WAI/WCAG22/quickref/
|
|
221
|
+
- Techniques for WCAG 2.2: https://www.w3.org/WAI/WCAG22/Techniques/
|