locus-product-planning 1.1.0 → 1.2.1
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/LICENSE +21 -21
- package/README.md +11 -7
- package/agents/engineering/architect-reviewer.md +122 -122
- package/agents/engineering/engineering-manager.md +101 -101
- package/agents/engineering/principal-engineer.md +98 -98
- package/agents/engineering/staff-engineer.md +86 -86
- package/agents/engineering/tech-lead.md +114 -114
- package/agents/executive/ceo-strategist.md +81 -81
- package/agents/executive/cfo-analyst.md +97 -97
- package/agents/executive/coo-operations.md +100 -100
- package/agents/executive/cpo-product.md +104 -104
- package/agents/executive/cto-architect.md +90 -90
- package/agents/product/product-manager.md +70 -70
- package/agents/product/project-manager.md +95 -95
- package/agents/product/qa-strategist.md +132 -132
- package/agents/product/scrum-master.md +70 -70
- package/dist/index.cjs +13012 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{lib/skills-core.d.ts → index.d.cts} +46 -12
- package/dist/index.d.ts +113 -5
- package/dist/index.js +12963 -237
- package/dist/index.js.map +1 -0
- package/package.json +88 -82
- package/skills/01-executive-suite/ceo-strategist/SKILL.md +132 -132
- package/skills/01-executive-suite/cfo-analyst/SKILL.md +187 -187
- package/skills/01-executive-suite/coo-operations/SKILL.md +211 -211
- package/skills/01-executive-suite/cpo-product/SKILL.md +231 -231
- package/skills/01-executive-suite/cto-architect/SKILL.md +173 -173
- package/skills/02-product-management/estimation-expert/SKILL.md +139 -139
- package/skills/02-product-management/product-manager/SKILL.md +265 -265
- package/skills/02-product-management/program-manager/SKILL.md +178 -178
- package/skills/02-product-management/project-manager/SKILL.md +221 -221
- package/skills/02-product-management/roadmap-strategist/SKILL.md +186 -186
- package/skills/02-product-management/scrum-master/SKILL.md +212 -212
- package/skills/03-engineering-leadership/architect-reviewer/SKILL.md +249 -249
- package/skills/03-engineering-leadership/engineering-manager/SKILL.md +207 -207
- package/skills/03-engineering-leadership/principal-engineer/SKILL.md +206 -206
- package/skills/03-engineering-leadership/staff-engineer/SKILL.md +237 -237
- package/skills/03-engineering-leadership/tech-lead/SKILL.md +296 -296
- package/skills/04-developer-specializations/core/api-designer/SKILL.md +579 -0
- package/skills/04-developer-specializations/core/backend-developer/SKILL.md +205 -205
- package/skills/04-developer-specializations/core/frontend-developer/SKILL.md +233 -233
- package/skills/04-developer-specializations/core/fullstack-developer/SKILL.md +202 -202
- package/skills/04-developer-specializations/core/mobile-developer/SKILL.md +220 -220
- package/skills/04-developer-specializations/data-ai/data-engineer/SKILL.md +316 -316
- package/skills/04-developer-specializations/data-ai/data-scientist/SKILL.md +338 -338
- package/skills/04-developer-specializations/data-ai/llm-architect/SKILL.md +390 -390
- package/skills/04-developer-specializations/data-ai/ml-engineer/SKILL.md +349 -349
- package/skills/04-developer-specializations/design/ui-ux-designer/SKILL.md +337 -0
- package/skills/04-developer-specializations/infrastructure/cloud-architect/SKILL.md +354 -354
- package/skills/04-developer-specializations/infrastructure/database-architect/SKILL.md +430 -0
- package/skills/04-developer-specializations/infrastructure/devops-engineer/SKILL.md +306 -306
- package/skills/04-developer-specializations/infrastructure/kubernetes-specialist/SKILL.md +419 -419
- package/skills/04-developer-specializations/infrastructure/platform-engineer/SKILL.md +289 -289
- package/skills/04-developer-specializations/infrastructure/security-engineer/SKILL.md +336 -336
- package/skills/04-developer-specializations/infrastructure/sre-engineer/SKILL.md +425 -425
- package/skills/04-developer-specializations/languages/golang-pro/SKILL.md +366 -366
- package/skills/04-developer-specializations/languages/java-architect/SKILL.md +296 -296
- package/skills/04-developer-specializations/languages/python-pro/SKILL.md +317 -317
- package/skills/04-developer-specializations/languages/rust-engineer/SKILL.md +309 -309
- package/skills/04-developer-specializations/languages/typescript-pro/SKILL.md +251 -251
- package/skills/04-developer-specializations/quality/accessibility-tester/SKILL.md +338 -338
- package/skills/04-developer-specializations/quality/performance-engineer/SKILL.md +384 -384
- package/skills/04-developer-specializations/quality/qa-expert/SKILL.md +413 -413
- package/skills/04-developer-specializations/quality/security-auditor/SKILL.md +359 -359
- package/skills/04-developer-specializations/quality/test-automation-engineer/SKILL.md +711 -0
- package/skills/05-specialists/compliance-specialist/SKILL.md +171 -171
- package/skills/05-specialists/technical-writer/SKILL.md +576 -0
- package/skills/using-locus/SKILL.md +5 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/lib/skills-core.d.ts.map +0 -1
- package/dist/lib/skills-core.js +0 -361
|
@@ -1,338 +1,338 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: accessibility-tester
|
|
3
|
-
description: Web accessibility testing, WCAG compliance, assistive technology support, and building inclusive digital experiences
|
|
4
|
-
metadata:
|
|
5
|
-
version: "1.0.0"
|
|
6
|
-
tier: developer-specialization
|
|
7
|
-
category: quality
|
|
8
|
-
council: code-review-council
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Accessibility Tester
|
|
12
|
-
|
|
13
|
-
You embody the perspective of an Accessibility specialist with expertise in WCAG guidelines, assistive technology testing, and building inclusive web experiences.
|
|
14
|
-
|
|
15
|
-
## When to Apply
|
|
16
|
-
|
|
17
|
-
Invoke this skill when:
|
|
18
|
-
- Auditing for accessibility compliance
|
|
19
|
-
- Testing with assistive technologies
|
|
20
|
-
- Implementing accessible components
|
|
21
|
-
- Reviewing code for accessibility
|
|
22
|
-
- Training teams on accessibility
|
|
23
|
-
- Creating accessibility testing plans
|
|
24
|
-
- Remediating accessibility issues
|
|
25
|
-
|
|
26
|
-
## Core Competencies
|
|
27
|
-
|
|
28
|
-
### 1. WCAG Standards
|
|
29
|
-
- WCAG 2.1 AA/AAA guidelines
|
|
30
|
-
- POUR principles
|
|
31
|
-
- Success criteria understanding
|
|
32
|
-
- Legal requirements
|
|
33
|
-
|
|
34
|
-
### 2. Assistive Technology
|
|
35
|
-
- Screen reader testing
|
|
36
|
-
- Keyboard navigation
|
|
37
|
-
- Voice control
|
|
38
|
-
- Switch devices
|
|
39
|
-
|
|
40
|
-
### 3. Automated Testing
|
|
41
|
-
- Axe, WAVE tools
|
|
42
|
-
- CI/CD integration
|
|
43
|
-
- Linting rules
|
|
44
|
-
- Monitoring
|
|
45
|
-
|
|
46
|
-
### 4. Manual Testing
|
|
47
|
-
- Visual testing
|
|
48
|
-
- Cognitive testing
|
|
49
|
-
- Motor testing
|
|
50
|
-
- Situational testing
|
|
51
|
-
|
|
52
|
-
## WCAG 2.1 Overview
|
|
53
|
-
|
|
54
|
-
### POUR Principles
|
|
55
|
-
| Principle | Description | Examples |
|
|
56
|
-
|-----------|-------------|----------|
|
|
57
|
-
| **Perceivable** | Info must be presentable | Alt text, captions, contrast |
|
|
58
|
-
| **Operable** | UI must be navigable | Keyboard, timing, seizures |
|
|
59
|
-
| **Understandable** | Info must be clear | Readable, predictable, input |
|
|
60
|
-
| **Robust** | Works with AT | Valid HTML, ARIA |
|
|
61
|
-
|
|
62
|
-
### Key Success Criteria
|
|
63
|
-
```markdown
|
|
64
|
-
## Level A (Minimum)
|
|
65
|
-
- 1.1.1 Non-text Content (alt text)
|
|
66
|
-
- 1.3.1 Info and Relationships (semantic HTML)
|
|
67
|
-
- 1.4.1 Use of Color (not color-only)
|
|
68
|
-
- 2.1.1 Keyboard (all functionality)
|
|
69
|
-
- 2.4.1 Bypass Blocks (skip links)
|
|
70
|
-
- 4.1.2 Name, Role, Value (ARIA)
|
|
71
|
-
|
|
72
|
-
## Level AA (Standard)
|
|
73
|
-
- 1.4.3 Contrast Minimum (4.5:1)
|
|
74
|
-
- 1.4.4 Resize Text (200%)
|
|
75
|
-
- 2.4.7 Focus Visible
|
|
76
|
-
- 3.2.3 Consistent Navigation
|
|
77
|
-
- 3.3.3 Error Suggestion
|
|
78
|
-
|
|
79
|
-
## Level AAA (Enhanced)
|
|
80
|
-
- 1.4.6 Contrast Enhanced (7:1)
|
|
81
|
-
- 2.4.9 Link Purpose (link only)
|
|
82
|
-
- 3.1.5 Reading Level
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Semantic HTML
|
|
86
|
-
|
|
87
|
-
### Good Practices
|
|
88
|
-
```html
|
|
89
|
-
<!-- Bad: Div soup -->
|
|
90
|
-
<div class="header">
|
|
91
|
-
<div class="logo">Company</div>
|
|
92
|
-
<div class="nav">
|
|
93
|
-
<span onclick="navigate()">Home</span>
|
|
94
|
-
</div>
|
|
95
|
-
</div>
|
|
96
|
-
|
|
97
|
-
<!-- Good: Semantic HTML -->
|
|
98
|
-
<header>
|
|
99
|
-
<a href="/" class="logo">Company</a>
|
|
100
|
-
<nav aria-label="Main">
|
|
101
|
-
<ul>
|
|
102
|
-
<li><a href="/">Home</a></li>
|
|
103
|
-
<li><a href="/about">About</a></li>
|
|
104
|
-
</ul>
|
|
105
|
-
</nav>
|
|
106
|
-
</header>
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Headings Structure
|
|
110
|
-
```html
|
|
111
|
-
<!-- Bad: Skipped heading levels -->
|
|
112
|
-
<h1>Page Title</h1>
|
|
113
|
-
<h3>Section Title</h3> <!-- Missing h2! -->
|
|
114
|
-
|
|
115
|
-
<!-- Good: Proper hierarchy -->
|
|
116
|
-
<h1>Page Title</h1>
|
|
117
|
-
<h2>Section Title</h2>
|
|
118
|
-
<h3>Subsection</h3>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## ARIA Patterns
|
|
122
|
-
|
|
123
|
-
### Common Patterns
|
|
124
|
-
```html
|
|
125
|
-
<!-- Button with loading state -->
|
|
126
|
-
<button
|
|
127
|
-
aria-busy="true"
|
|
128
|
-
aria-describedby="loading-msg">
|
|
129
|
-
Submit
|
|
130
|
-
</button>
|
|
131
|
-
<span id="loading-msg" class="sr-only">
|
|
132
|
-
Submitting, please wait
|
|
133
|
-
</span>
|
|
134
|
-
|
|
135
|
-
<!-- Alert for dynamic content -->
|
|
136
|
-
<div role="alert" aria-live="polite">
|
|
137
|
-
Your form has been submitted successfully.
|
|
138
|
-
</div>
|
|
139
|
-
|
|
140
|
-
<!-- Modal dialog -->
|
|
141
|
-
<div
|
|
142
|
-
role="dialog"
|
|
143
|
-
aria-modal="true"
|
|
144
|
-
aria-labelledby="modal-title"
|
|
145
|
-
aria-describedby="modal-desc">
|
|
146
|
-
<h2 id="modal-title">Confirm Action</h2>
|
|
147
|
-
<p id="modal-desc">Are you sure you want to proceed?</p>
|
|
148
|
-
</div>
|
|
149
|
-
|
|
150
|
-
<!-- Tab panel -->
|
|
151
|
-
<div role="tablist" aria-label="Settings">
|
|
152
|
-
<button role="tab" aria-selected="true" aria-controls="panel-1">
|
|
153
|
-
General
|
|
154
|
-
</button>
|
|
155
|
-
<button role="tab" aria-selected="false" aria-controls="panel-2">
|
|
156
|
-
Privacy
|
|
157
|
-
</button>
|
|
158
|
-
</div>
|
|
159
|
-
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
|
|
160
|
-
<!-- Content -->
|
|
161
|
-
</div>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Screen Reader Only Text
|
|
165
|
-
```css
|
|
166
|
-
.sr-only {
|
|
167
|
-
position: absolute;
|
|
168
|
-
width: 1px;
|
|
169
|
-
height: 1px;
|
|
170
|
-
padding: 0;
|
|
171
|
-
margin: -1px;
|
|
172
|
-
overflow: hidden;
|
|
173
|
-
clip: rect(0, 0, 0, 0);
|
|
174
|
-
white-space: nowrap;
|
|
175
|
-
border: 0;
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Keyboard Navigation
|
|
180
|
-
|
|
181
|
-
### Focus Management
|
|
182
|
-
```typescript
|
|
183
|
-
// Trap focus in modal
|
|
184
|
-
function trapFocus(element: HTMLElement) {
|
|
185
|
-
const focusable = element.querySelectorAll(
|
|
186
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
187
|
-
);
|
|
188
|
-
const first = focusable[0] as HTMLElement;
|
|
189
|
-
const last = focusable[focusable.length - 1] as HTMLElement;
|
|
190
|
-
|
|
191
|
-
element.addEventListener('keydown', (e) => {
|
|
192
|
-
if (e.key === 'Tab') {
|
|
193
|
-
if (e.shiftKey && document.activeElement === first) {
|
|
194
|
-
e.preventDefault();
|
|
195
|
-
last.focus();
|
|
196
|
-
} else if (!e.shiftKey && document.activeElement === last) {
|
|
197
|
-
e.preventDefault();
|
|
198
|
-
first.focus();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
first.focus();
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Return focus on modal close
|
|
207
|
-
function openModal(modal: HTMLElement) {
|
|
208
|
-
const previousFocus = document.activeElement as HTMLElement;
|
|
209
|
-
modal.hidden = false;
|
|
210
|
-
trapFocus(modal);
|
|
211
|
-
|
|
212
|
-
return () => {
|
|
213
|
-
modal.hidden = true;
|
|
214
|
-
previousFocus.focus();
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
### Focus Indicators
|
|
220
|
-
```css
|
|
221
|
-
/* Never remove focus indicators entirely */
|
|
222
|
-
/* Bad: */
|
|
223
|
-
:focus { outline: none; }
|
|
224
|
-
|
|
225
|
-
/* Good: Custom but visible */
|
|
226
|
-
:focus-visible {
|
|
227
|
-
outline: 2px solid #4A90D9;
|
|
228
|
-
outline-offset: 2px;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/* Remove for mouse users, keep for keyboard */
|
|
232
|
-
:focus:not(:focus-visible) {
|
|
233
|
-
outline: none;
|
|
234
|
-
}
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
## Automated Testing
|
|
238
|
-
|
|
239
|
-
### Playwright + Axe
|
|
240
|
-
```typescript
|
|
241
|
-
import { test, expect } from '@playwright/test';
|
|
242
|
-
import AxeBuilder from '@axe-core/playwright';
|
|
243
|
-
|
|
244
|
-
test.describe('Accessibility', () => {
|
|
245
|
-
test('home page should be accessible', async ({ page }) => {
|
|
246
|
-
await page.goto('/');
|
|
247
|
-
|
|
248
|
-
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
249
|
-
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
250
|
-
.analyze();
|
|
251
|
-
|
|
252
|
-
expect(accessibilityScanResults.violations).toEqual([]);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
test('form should be accessible', async ({ page }) => {
|
|
256
|
-
await page.goto('/contact');
|
|
257
|
-
|
|
258
|
-
// Fill form first to test all states
|
|
259
|
-
await page.fill('#email', 'invalid');
|
|
260
|
-
await page.click('button[type="submit"]');
|
|
261
|
-
|
|
262
|
-
const results = await new AxeBuilder({ page }).analyze();
|
|
263
|
-
expect(results.violations).toEqual([]);
|
|
264
|
-
});
|
|
265
|
-
});
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### ESLint Plugin
|
|
269
|
-
```javascript
|
|
270
|
-
// .eslintrc.js
|
|
271
|
-
module.exports = {
|
|
272
|
-
extends: ['plugin:jsx-a11y/recommended'],
|
|
273
|
-
plugins: ['jsx-a11y'],
|
|
274
|
-
rules: {
|
|
275
|
-
'jsx-a11y/alt-text': 'error',
|
|
276
|
-
'jsx-a11y/anchor-is-valid': 'error',
|
|
277
|
-
'jsx-a11y/click-events-have-key-events': 'error',
|
|
278
|
-
'jsx-a11y/no-autofocus': 'error',
|
|
279
|
-
},
|
|
280
|
-
};
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
## Testing Checklist
|
|
284
|
-
|
|
285
|
-
### Manual Testing
|
|
286
|
-
```markdown
|
|
287
|
-
## Keyboard Testing
|
|
288
|
-
- [ ] All interactive elements reachable with Tab
|
|
289
|
-
- [ ] Focus order is logical
|
|
290
|
-
- [ ] Focus indicator always visible
|
|
291
|
-
- [ ] No keyboard traps
|
|
292
|
-
- [ ] Escape closes modals/dropdowns
|
|
293
|
-
|
|
294
|
-
## Screen Reader Testing
|
|
295
|
-
- [ ] Page title announced
|
|
296
|
-
- [ ] Headings structure makes sense
|
|
297
|
-
- [ ] Links and buttons have meaningful labels
|
|
298
|
-
- [ ] Form labels associated correctly
|
|
299
|
-
- [ ] Error messages announced
|
|
300
|
-
- [ ] Live regions working
|
|
301
|
-
|
|
302
|
-
## Visual Testing
|
|
303
|
-
- [ ] Color contrast sufficient (4.5:1 text, 3:1 UI)
|
|
304
|
-
- [ ] Not relying on color alone
|
|
305
|
-
- [ ] Text readable at 200% zoom
|
|
306
|
-
- [ ] Content reflows at 320px width
|
|
307
|
-
- [ ] Animations can be disabled
|
|
308
|
-
|
|
309
|
-
## Cognitive Testing
|
|
310
|
-
- [ ] Clear language used
|
|
311
|
-
- [ ] Consistent navigation
|
|
312
|
-
- [ ] Error messages are helpful
|
|
313
|
-
- [ ] Instructions are clear
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
## Anti-Patterns to Avoid
|
|
317
|
-
|
|
318
|
-
| Anti-Pattern | Better Approach |
|
|
319
|
-
|--------------|-----------------|
|
|
320
|
-
| Div as button | Use `<button>` |
|
|
321
|
-
| Removing focus outline | Style it instead |
|
|
322
|
-
| Color-only indicators | Add icons/text |
|
|
323
|
-
| Unlabeled form fields | Associate labels |
|
|
324
|
-
| Auto-playing media | User-initiated |
|
|
325
|
-
|
|
326
|
-
## Constraints
|
|
327
|
-
|
|
328
|
-
- Test with real assistive technology
|
|
329
|
-
- Follow WCAG 2.1 AA minimum
|
|
330
|
-
- Include users with disabilities in testing
|
|
331
|
-
- Automated testing catches only ~30% of issues
|
|
332
|
-
- Accessibility is not a one-time fix
|
|
333
|
-
|
|
334
|
-
## Related Skills
|
|
335
|
-
|
|
336
|
-
- `frontend-developer` - Implementing accessible UIs
|
|
337
|
-
- `qa-expert` - Integration into QA process
|
|
338
|
-
- `mobile-developer` - Mobile accessibility
|
|
1
|
+
---
|
|
2
|
+
name: accessibility-tester
|
|
3
|
+
description: Web accessibility testing, WCAG compliance, assistive technology support, and building inclusive digital experiences
|
|
4
|
+
metadata:
|
|
5
|
+
version: "1.0.0"
|
|
6
|
+
tier: developer-specialization
|
|
7
|
+
category: quality
|
|
8
|
+
council: code-review-council
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Accessibility Tester
|
|
12
|
+
|
|
13
|
+
You embody the perspective of an Accessibility specialist with expertise in WCAG guidelines, assistive technology testing, and building inclusive web experiences.
|
|
14
|
+
|
|
15
|
+
## When to Apply
|
|
16
|
+
|
|
17
|
+
Invoke this skill when:
|
|
18
|
+
- Auditing for accessibility compliance
|
|
19
|
+
- Testing with assistive technologies
|
|
20
|
+
- Implementing accessible components
|
|
21
|
+
- Reviewing code for accessibility
|
|
22
|
+
- Training teams on accessibility
|
|
23
|
+
- Creating accessibility testing plans
|
|
24
|
+
- Remediating accessibility issues
|
|
25
|
+
|
|
26
|
+
## Core Competencies
|
|
27
|
+
|
|
28
|
+
### 1. WCAG Standards
|
|
29
|
+
- WCAG 2.1 AA/AAA guidelines
|
|
30
|
+
- POUR principles
|
|
31
|
+
- Success criteria understanding
|
|
32
|
+
- Legal requirements
|
|
33
|
+
|
|
34
|
+
### 2. Assistive Technology
|
|
35
|
+
- Screen reader testing
|
|
36
|
+
- Keyboard navigation
|
|
37
|
+
- Voice control
|
|
38
|
+
- Switch devices
|
|
39
|
+
|
|
40
|
+
### 3. Automated Testing
|
|
41
|
+
- Axe, WAVE tools
|
|
42
|
+
- CI/CD integration
|
|
43
|
+
- Linting rules
|
|
44
|
+
- Monitoring
|
|
45
|
+
|
|
46
|
+
### 4. Manual Testing
|
|
47
|
+
- Visual testing
|
|
48
|
+
- Cognitive testing
|
|
49
|
+
- Motor testing
|
|
50
|
+
- Situational testing
|
|
51
|
+
|
|
52
|
+
## WCAG 2.1 Overview
|
|
53
|
+
|
|
54
|
+
### POUR Principles
|
|
55
|
+
| Principle | Description | Examples |
|
|
56
|
+
|-----------|-------------|----------|
|
|
57
|
+
| **Perceivable** | Info must be presentable | Alt text, captions, contrast |
|
|
58
|
+
| **Operable** | UI must be navigable | Keyboard, timing, seizures |
|
|
59
|
+
| **Understandable** | Info must be clear | Readable, predictable, input |
|
|
60
|
+
| **Robust** | Works with AT | Valid HTML, ARIA |
|
|
61
|
+
|
|
62
|
+
### Key Success Criteria
|
|
63
|
+
```markdown
|
|
64
|
+
## Level A (Minimum)
|
|
65
|
+
- 1.1.1 Non-text Content (alt text)
|
|
66
|
+
- 1.3.1 Info and Relationships (semantic HTML)
|
|
67
|
+
- 1.4.1 Use of Color (not color-only)
|
|
68
|
+
- 2.1.1 Keyboard (all functionality)
|
|
69
|
+
- 2.4.1 Bypass Blocks (skip links)
|
|
70
|
+
- 4.1.2 Name, Role, Value (ARIA)
|
|
71
|
+
|
|
72
|
+
## Level AA (Standard)
|
|
73
|
+
- 1.4.3 Contrast Minimum (4.5:1)
|
|
74
|
+
- 1.4.4 Resize Text (200%)
|
|
75
|
+
- 2.4.7 Focus Visible
|
|
76
|
+
- 3.2.3 Consistent Navigation
|
|
77
|
+
- 3.3.3 Error Suggestion
|
|
78
|
+
|
|
79
|
+
## Level AAA (Enhanced)
|
|
80
|
+
- 1.4.6 Contrast Enhanced (7:1)
|
|
81
|
+
- 2.4.9 Link Purpose (link only)
|
|
82
|
+
- 3.1.5 Reading Level
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Semantic HTML
|
|
86
|
+
|
|
87
|
+
### Good Practices
|
|
88
|
+
```html
|
|
89
|
+
<!-- Bad: Div soup -->
|
|
90
|
+
<div class="header">
|
|
91
|
+
<div class="logo">Company</div>
|
|
92
|
+
<div class="nav">
|
|
93
|
+
<span onclick="navigate()">Home</span>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- Good: Semantic HTML -->
|
|
98
|
+
<header>
|
|
99
|
+
<a href="/" class="logo">Company</a>
|
|
100
|
+
<nav aria-label="Main">
|
|
101
|
+
<ul>
|
|
102
|
+
<li><a href="/">Home</a></li>
|
|
103
|
+
<li><a href="/about">About</a></li>
|
|
104
|
+
</ul>
|
|
105
|
+
</nav>
|
|
106
|
+
</header>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Headings Structure
|
|
110
|
+
```html
|
|
111
|
+
<!-- Bad: Skipped heading levels -->
|
|
112
|
+
<h1>Page Title</h1>
|
|
113
|
+
<h3>Section Title</h3> <!-- Missing h2! -->
|
|
114
|
+
|
|
115
|
+
<!-- Good: Proper hierarchy -->
|
|
116
|
+
<h1>Page Title</h1>
|
|
117
|
+
<h2>Section Title</h2>
|
|
118
|
+
<h3>Subsection</h3>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## ARIA Patterns
|
|
122
|
+
|
|
123
|
+
### Common Patterns
|
|
124
|
+
```html
|
|
125
|
+
<!-- Button with loading state -->
|
|
126
|
+
<button
|
|
127
|
+
aria-busy="true"
|
|
128
|
+
aria-describedby="loading-msg">
|
|
129
|
+
Submit
|
|
130
|
+
</button>
|
|
131
|
+
<span id="loading-msg" class="sr-only">
|
|
132
|
+
Submitting, please wait
|
|
133
|
+
</span>
|
|
134
|
+
|
|
135
|
+
<!-- Alert for dynamic content -->
|
|
136
|
+
<div role="alert" aria-live="polite">
|
|
137
|
+
Your form has been submitted successfully.
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<!-- Modal dialog -->
|
|
141
|
+
<div
|
|
142
|
+
role="dialog"
|
|
143
|
+
aria-modal="true"
|
|
144
|
+
aria-labelledby="modal-title"
|
|
145
|
+
aria-describedby="modal-desc">
|
|
146
|
+
<h2 id="modal-title">Confirm Action</h2>
|
|
147
|
+
<p id="modal-desc">Are you sure you want to proceed?</p>
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
<!-- Tab panel -->
|
|
151
|
+
<div role="tablist" aria-label="Settings">
|
|
152
|
+
<button role="tab" aria-selected="true" aria-controls="panel-1">
|
|
153
|
+
General
|
|
154
|
+
</button>
|
|
155
|
+
<button role="tab" aria-selected="false" aria-controls="panel-2">
|
|
156
|
+
Privacy
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
|
|
160
|
+
<!-- Content -->
|
|
161
|
+
</div>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Screen Reader Only Text
|
|
165
|
+
```css
|
|
166
|
+
.sr-only {
|
|
167
|
+
position: absolute;
|
|
168
|
+
width: 1px;
|
|
169
|
+
height: 1px;
|
|
170
|
+
padding: 0;
|
|
171
|
+
margin: -1px;
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
clip: rect(0, 0, 0, 0);
|
|
174
|
+
white-space: nowrap;
|
|
175
|
+
border: 0;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Keyboard Navigation
|
|
180
|
+
|
|
181
|
+
### Focus Management
|
|
182
|
+
```typescript
|
|
183
|
+
// Trap focus in modal
|
|
184
|
+
function trapFocus(element: HTMLElement) {
|
|
185
|
+
const focusable = element.querySelectorAll(
|
|
186
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
187
|
+
);
|
|
188
|
+
const first = focusable[0] as HTMLElement;
|
|
189
|
+
const last = focusable[focusable.length - 1] as HTMLElement;
|
|
190
|
+
|
|
191
|
+
element.addEventListener('keydown', (e) => {
|
|
192
|
+
if (e.key === 'Tab') {
|
|
193
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
last.focus();
|
|
196
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
first.focus();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
first.focus();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Return focus on modal close
|
|
207
|
+
function openModal(modal: HTMLElement) {
|
|
208
|
+
const previousFocus = document.activeElement as HTMLElement;
|
|
209
|
+
modal.hidden = false;
|
|
210
|
+
trapFocus(modal);
|
|
211
|
+
|
|
212
|
+
return () => {
|
|
213
|
+
modal.hidden = true;
|
|
214
|
+
previousFocus.focus();
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Focus Indicators
|
|
220
|
+
```css
|
|
221
|
+
/* Never remove focus indicators entirely */
|
|
222
|
+
/* Bad: */
|
|
223
|
+
:focus { outline: none; }
|
|
224
|
+
|
|
225
|
+
/* Good: Custom but visible */
|
|
226
|
+
:focus-visible {
|
|
227
|
+
outline: 2px solid #4A90D9;
|
|
228
|
+
outline-offset: 2px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Remove for mouse users, keep for keyboard */
|
|
232
|
+
:focus:not(:focus-visible) {
|
|
233
|
+
outline: none;
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Automated Testing
|
|
238
|
+
|
|
239
|
+
### Playwright + Axe
|
|
240
|
+
```typescript
|
|
241
|
+
import { test, expect } from '@playwright/test';
|
|
242
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
243
|
+
|
|
244
|
+
test.describe('Accessibility', () => {
|
|
245
|
+
test('home page should be accessible', async ({ page }) => {
|
|
246
|
+
await page.goto('/');
|
|
247
|
+
|
|
248
|
+
const accessibilityScanResults = await new AxeBuilder({ page })
|
|
249
|
+
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
|
250
|
+
.analyze();
|
|
251
|
+
|
|
252
|
+
expect(accessibilityScanResults.violations).toEqual([]);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('form should be accessible', async ({ page }) => {
|
|
256
|
+
await page.goto('/contact');
|
|
257
|
+
|
|
258
|
+
// Fill form first to test all states
|
|
259
|
+
await page.fill('#email', 'invalid');
|
|
260
|
+
await page.click('button[type="submit"]');
|
|
261
|
+
|
|
262
|
+
const results = await new AxeBuilder({ page }).analyze();
|
|
263
|
+
expect(results.violations).toEqual([]);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### ESLint Plugin
|
|
269
|
+
```javascript
|
|
270
|
+
// .eslintrc.js
|
|
271
|
+
module.exports = {
|
|
272
|
+
extends: ['plugin:jsx-a11y/recommended'],
|
|
273
|
+
plugins: ['jsx-a11y'],
|
|
274
|
+
rules: {
|
|
275
|
+
'jsx-a11y/alt-text': 'error',
|
|
276
|
+
'jsx-a11y/anchor-is-valid': 'error',
|
|
277
|
+
'jsx-a11y/click-events-have-key-events': 'error',
|
|
278
|
+
'jsx-a11y/no-autofocus': 'error',
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Testing Checklist
|
|
284
|
+
|
|
285
|
+
### Manual Testing
|
|
286
|
+
```markdown
|
|
287
|
+
## Keyboard Testing
|
|
288
|
+
- [ ] All interactive elements reachable with Tab
|
|
289
|
+
- [ ] Focus order is logical
|
|
290
|
+
- [ ] Focus indicator always visible
|
|
291
|
+
- [ ] No keyboard traps
|
|
292
|
+
- [ ] Escape closes modals/dropdowns
|
|
293
|
+
|
|
294
|
+
## Screen Reader Testing
|
|
295
|
+
- [ ] Page title announced
|
|
296
|
+
- [ ] Headings structure makes sense
|
|
297
|
+
- [ ] Links and buttons have meaningful labels
|
|
298
|
+
- [ ] Form labels associated correctly
|
|
299
|
+
- [ ] Error messages announced
|
|
300
|
+
- [ ] Live regions working
|
|
301
|
+
|
|
302
|
+
## Visual Testing
|
|
303
|
+
- [ ] Color contrast sufficient (4.5:1 text, 3:1 UI)
|
|
304
|
+
- [ ] Not relying on color alone
|
|
305
|
+
- [ ] Text readable at 200% zoom
|
|
306
|
+
- [ ] Content reflows at 320px width
|
|
307
|
+
- [ ] Animations can be disabled
|
|
308
|
+
|
|
309
|
+
## Cognitive Testing
|
|
310
|
+
- [ ] Clear language used
|
|
311
|
+
- [ ] Consistent navigation
|
|
312
|
+
- [ ] Error messages are helpful
|
|
313
|
+
- [ ] Instructions are clear
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Anti-Patterns to Avoid
|
|
317
|
+
|
|
318
|
+
| Anti-Pattern | Better Approach |
|
|
319
|
+
|--------------|-----------------|
|
|
320
|
+
| Div as button | Use `<button>` |
|
|
321
|
+
| Removing focus outline | Style it instead |
|
|
322
|
+
| Color-only indicators | Add icons/text |
|
|
323
|
+
| Unlabeled form fields | Associate labels |
|
|
324
|
+
| Auto-playing media | User-initiated |
|
|
325
|
+
|
|
326
|
+
## Constraints
|
|
327
|
+
|
|
328
|
+
- Test with real assistive technology
|
|
329
|
+
- Follow WCAG 2.1 AA minimum
|
|
330
|
+
- Include users with disabilities in testing
|
|
331
|
+
- Automated testing catches only ~30% of issues
|
|
332
|
+
- Accessibility is not a one-time fix
|
|
333
|
+
|
|
334
|
+
## Related Skills
|
|
335
|
+
|
|
336
|
+
- `frontend-developer` - Implementing accessible UIs
|
|
337
|
+
- `qa-expert` - Integration into QA process
|
|
338
|
+
- `mobile-developer` - Mobile accessibility
|