odd-studio 1.0.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/.claude-plugin/plugin.json +19 -0
- package/README.md +229 -0
- package/bin/odd-studio.js +212 -0
- package/hooks/odd-destructive-guard.sh +98 -0
- package/hooks/odd-git-safety.sh +138 -0
- package/hooks/odd-outcome-quality.sh +84 -0
- package/hooks/odd-pre-build.sh +57 -0
- package/hooks/odd-session-save.sh +38 -0
- package/hooks/odd-ui-check.sh +69 -0
- package/package.json +43 -0
- package/scripts/install-skill.js +30 -0
- package/scripts/postinstall.js +28 -0
- package/scripts/scaffold-project.js +61 -0
- package/scripts/setup-hooks.js +105 -0
- package/skill/SKILL.md +464 -0
- package/skill/docs/build/build-protocol.md +532 -0
- package/skill/docs/kb/odd-kb.md +462 -0
- package/skill/docs/planning/build-planner.md +315 -0
- package/skill/docs/planning/outcome-writer.md +328 -0
- package/skill/docs/planning/persona-architect.md +258 -0
- package/skill/docs/planning/systems-mapper.md +270 -0
- package/skill/docs/ui/accessibility.md +415 -0
- package/skill/docs/ui/component-guide.md +356 -0
- package/skill/docs/ui/design-system.md +403 -0
- package/templates/.odd/state.json +16 -0
- package/templates/CLAUDE.md +93 -0
- package/templates/docs/contract-map.md +60 -0
- package/templates/docs/outcomes/.gitkeep +0 -0
- package/templates/docs/outcomes/example-outcome.md +104 -0
- package/templates/docs/personas/.gitkeep +0 -0
- package/templates/docs/personas/example-persona.md +108 -0
- package/templates/docs/plan.md +73 -0
- package/templates/docs/ui/.gitkeep +0 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# ODD Studio — Accessibility Verification Protocol
|
|
2
|
+
|
|
3
|
+
Accessibility is the practice of building software that works for everyone — including people who use keyboards instead of mice, screen readers instead of displays, or who have configured their devices to reduce motion or increase text size.
|
|
4
|
+
|
|
5
|
+
This document is written for domain experts, not developers. You do not need to understand the code to verify accessibility. You need to know what to look for and how to look for it.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Why Accessibility Matters to You
|
|
10
|
+
|
|
11
|
+
### The regulatory reason
|
|
12
|
+
|
|
13
|
+
In the United Kingdom, the Equality Act 2010 requires that digital services not discriminate against disabled people. The Public Sector Bodies Accessibility Regulations 2018 mandate WCAG 2.1 AA compliance for public sector websites. Many private sector obligations follow from the Equality Act. If your platform serves students, employees, patients, or the public, you have legal obligations around accessibility.
|
|
14
|
+
|
|
15
|
+
In the United States, the ADA (Americans with Disabilities Act) has been interpreted to cover websites. Title III cases are regularly filed against organisations whose digital products are inaccessible.
|
|
16
|
+
|
|
17
|
+
Inaccessibility is not a technical edge case. It is a legal liability and a barrier to the people you are trying to serve.
|
|
18
|
+
|
|
19
|
+
### The moral reason
|
|
20
|
+
|
|
21
|
+
Approximately 1 in 5 people in the UK has a disability of some kind. Of those, many experience difficulty using software that was not designed with them in mind: people who are blind or have low vision, people with motor disabilities who cannot use a mouse, people with cognitive disabilities who struggle with complex or confusing interfaces, people with hearing loss who cannot rely on audio cues.
|
|
22
|
+
|
|
23
|
+
When you build inaccessible software in a domain you care about — education, healthcare, housing, professional services — you are excluding the people in that domain who already face the most barriers.
|
|
24
|
+
|
|
25
|
+
### The practical reason
|
|
26
|
+
|
|
27
|
+
Accessible design is better design for everyone.
|
|
28
|
+
|
|
29
|
+
- Keyboard navigation makes power users faster
|
|
30
|
+
- High-contrast text is easier to read in sunlight or on cheap monitors
|
|
31
|
+
- Clear error messages help everyone, not just people with cognitive disabilities
|
|
32
|
+
- Large touch targets are easier to use for everyone, not just people with motor impairments
|
|
33
|
+
- Captions on video benefit people in noisy environments, not just deaf viewers
|
|
34
|
+
|
|
35
|
+
When you build for accessibility, you build for edge cases. Edge cases reveal design weaknesses that affect everyone.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## The WCAG 2.1 AA Standard
|
|
40
|
+
|
|
41
|
+
WCAG stands for Web Content Accessibility Guidelines. It is published by the World Wide Web Consortium (W3C) and is the internationally recognised standard for web accessibility.
|
|
42
|
+
|
|
43
|
+
Version 2.1 Level AA is the standard most organisations target. It is what regulators check. It is what ODD Studio verifies.
|
|
44
|
+
|
|
45
|
+
WCAG is organised around 4 principles. Every guideline belongs to one.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## The 4 Principles
|
|
50
|
+
|
|
51
|
+
### Perceivable
|
|
52
|
+
|
|
53
|
+
Information must be presentable to users in ways they can perceive. If a user cannot see, the information must be available to them another way.
|
|
54
|
+
|
|
55
|
+
**In a real domain example:** Imagine you are building an education platform. A student who is blind uses a screen reader — software that reads the page aloud. If your student application form only communicates status through colour (red = error, green = success), that student cannot perceive the status. The principle of Perceivable requires that you also communicate status through text.
|
|
56
|
+
|
|
57
|
+
**What this means in practice:**
|
|
58
|
+
- Images have descriptive text alternatives
|
|
59
|
+
- Colour is never the only way to communicate information
|
|
60
|
+
- Text can be resized up to 200% without losing functionality
|
|
61
|
+
- Audio and video have captions or transcripts
|
|
62
|
+
|
|
63
|
+
### Operable
|
|
64
|
+
|
|
65
|
+
Interface components must be operable by users who cannot use a mouse.
|
|
66
|
+
|
|
67
|
+
**In a real domain example:** A senior student who has arthritis navigates your platform using only a keyboard. She presses Tab to move between form fields and Enter to submit. If your Submit button cannot be reached by Tab, or if a dropdown menu only works with a mouse hover, she cannot complete the application. The principle of Operable requires that every interactive element be reachable and usable by keyboard.
|
|
68
|
+
|
|
69
|
+
**What this means in practice:**
|
|
70
|
+
- All functionality is available from a keyboard
|
|
71
|
+
- No content causes seizures (no flashing more than 3 times per second)
|
|
72
|
+
- Users can pause, stop, or hide any moving or auto-updating content
|
|
73
|
+
- Users have enough time to read and use content (timeouts warn and can be extended)
|
|
74
|
+
|
|
75
|
+
### Understandable
|
|
76
|
+
|
|
77
|
+
Information and interface operation must be understandable.
|
|
78
|
+
|
|
79
|
+
**In a real domain example:** A teacher submits a course update form. It fails validation. The error message reads: "ERR_422: Field constraint violation on entity.courseTitle". The teacher does not know what to do. The principle of Understandable requires that error messages be written in language the user can act on.
|
|
80
|
+
|
|
81
|
+
**What this means in practice:**
|
|
82
|
+
- Text is readable and understandable (appropriate language, no unexplained jargon)
|
|
83
|
+
- Content behaves predictably — navigation in the same position, consistent component behaviour
|
|
84
|
+
- Error messages identify the problem and suggest a fix
|
|
85
|
+
- Form instructions appear before the form, not only in placeholder text
|
|
86
|
+
|
|
87
|
+
### Robust
|
|
88
|
+
|
|
89
|
+
Content must be robust enough to be interpreted by a wide variety of user agents, including assistive technologies.
|
|
90
|
+
|
|
91
|
+
**In a real domain example:** A student uses an older version of a screen reader that expects standard HTML semantics. If your developer built the "button" as a styled `<div>` element without ARIA roles, the screen reader cannot identify it as a button and cannot interact with it. The principle of Robust requires that you use standard, semantic HTML or supplement non-standard elements with correct ARIA roles.
|
|
92
|
+
|
|
93
|
+
**What this means in practice:**
|
|
94
|
+
- HTML elements are used according to their purpose (buttons for actions, links for navigation)
|
|
95
|
+
- All interactive components have name, role, and value exposed to assistive technologies
|
|
96
|
+
- Status messages are programmatically determinable (the screen reader can find them)
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Step-by-Step Verification Procedure
|
|
101
|
+
|
|
102
|
+
You can run all of these tests in a standard browser without any additional software (except screen reader testing, which requires a free download).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### Test 1 — Keyboard-Only Navigation
|
|
107
|
+
|
|
108
|
+
**What you are testing:** Can a user complete every task on this screen using only a keyboard?
|
|
109
|
+
|
|
110
|
+
**How to do it:**
|
|
111
|
+
|
|
112
|
+
1. Put your mouse to one side. Do not touch it during this test.
|
|
113
|
+
2. Load the page you are testing.
|
|
114
|
+
3. Press Tab. A visible focus ring should appear on the first interactive element.
|
|
115
|
+
4. Keep pressing Tab. Observe:
|
|
116
|
+
- Does the focus ring move to every interactive element? (buttons, links, form fields, dropdown triggers)
|
|
117
|
+
- Does it move in a logical order? (top to bottom, left to right — following the visual layout)
|
|
118
|
+
- Is the focus ring clearly visible at every step?
|
|
119
|
+
5. When focus is on a button, press Enter or Space. Does the button activate?
|
|
120
|
+
6. When focus is on a link, press Enter. Does it navigate?
|
|
121
|
+
7. When focus is on a Select or Dropdown, press Enter to open it, arrow keys to navigate, Enter to select.
|
|
122
|
+
8. When a Dialog opens, confirm focus moves into the dialog. Press Tab inside the dialog — confirm focus stays within the dialog (it should not escape to the page behind). Press Escape — confirm the dialog closes and focus returns to the element that opened it.
|
|
123
|
+
|
|
124
|
+
**Pass criteria:**
|
|
125
|
+
- Every interactive element reachable by Tab
|
|
126
|
+
- Focus ring visible at every step
|
|
127
|
+
- All actions completable by keyboard
|
|
128
|
+
- Dialogs trap and return focus correctly
|
|
129
|
+
|
|
130
|
+
**Fail example:** "When I Tab to the Approve button on the Applications screen and press Enter, the confirmation dialog opens but focus stays on the button behind the dialog. I cannot reach the Confirm button without using the mouse."
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### Test 2 — Screen Reader Spot-Check
|
|
135
|
+
|
|
136
|
+
**What you are testing:** Does the page make sense when read aloud by a screen reader?
|
|
137
|
+
|
|
138
|
+
**What you need:**
|
|
139
|
+
- On macOS: VoiceOver is built in. Press Cmd+F5 to turn it on.
|
|
140
|
+
- On Windows: NVDA is free. Download from nvaccess.org.
|
|
141
|
+
- On iOS: VoiceOver is built in. Settings > Accessibility > VoiceOver.
|
|
142
|
+
|
|
143
|
+
**How to do it (VoiceOver on macOS):**
|
|
144
|
+
|
|
145
|
+
1. Press Cmd+F5 to start VoiceOver.
|
|
146
|
+
2. Press Ctrl+Option+Right Arrow to move to the next element on the page.
|
|
147
|
+
3. Listen to what VoiceOver reads. Evaluate:
|
|
148
|
+
- Does each form field have a clear label? ("Application date, date field" — not just "date field")
|
|
149
|
+
- Do buttons have meaningful names? ("Approve application from Sarah Chen" — not just "button" or "icon")
|
|
150
|
+
- Do images have appropriate descriptions? (or are decorative images skipped silently?)
|
|
151
|
+
- When an error appears, does VoiceOver announce it? (without you having to navigate to it)
|
|
152
|
+
- When a success toast appears, does VoiceOver announce it?
|
|
153
|
+
4. Press Cmd+F5 to stop VoiceOver.
|
|
154
|
+
|
|
155
|
+
You do not need to navigate the entire page. Spot-check the most important elements: forms, primary actions, status indicators, and notifications.
|
|
156
|
+
|
|
157
|
+
**Pass criteria:**
|
|
158
|
+
- Every form field announces its label
|
|
159
|
+
- Every button announces its purpose (not just "button")
|
|
160
|
+
- Errors and success messages are announced automatically
|
|
161
|
+
- Decorative images are silent
|
|
162
|
+
|
|
163
|
+
**Fail example:** "When I reach the Approve button with VoiceOver, it reads 'button'. It does not say what the button does or which application it refers to. I cannot use this without the mouse."
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Test 3 — Colour Contrast Check
|
|
168
|
+
|
|
169
|
+
**What you are testing:** Is the text readable for people with colour vision deficiencies or in low-contrast conditions?
|
|
170
|
+
|
|
171
|
+
**How to do it (Chrome DevTools):**
|
|
172
|
+
|
|
173
|
+
1. Open Chrome DevTools (F12 or Cmd+Option+I).
|
|
174
|
+
2. Click the Elements tab.
|
|
175
|
+
3. Click the cursor icon (Inspect Element) and click on a piece of body text.
|
|
176
|
+
4. In the Styles panel on the right, find the `color` property.
|
|
177
|
+
5. Click the colour swatch next to the value.
|
|
178
|
+
6. A colour picker opens. At the bottom, it shows the contrast ratio against the background.
|
|
179
|
+
7. The ratio must be 4.5:1 or higher for normal text, 3:1 or higher for large text (18px bold or 24px regular).
|
|
180
|
+
|
|
181
|
+
**Also check:**
|
|
182
|
+
- Placeholder text in form fields (this commonly fails — placeholder text is often too light)
|
|
183
|
+
- Muted/secondary text
|
|
184
|
+
- Text on coloured backgrounds (badges, buttons, alerts)
|
|
185
|
+
- Link text within body copy
|
|
186
|
+
|
|
187
|
+
**Pass criteria:**
|
|
188
|
+
- Normal text: contrast ratio 4.5:1 or above
|
|
189
|
+
- Large text: contrast ratio 3:1 or above
|
|
190
|
+
- No information conveyed by colour alone
|
|
191
|
+
|
|
192
|
+
**Fail example:** "The 'Pending' status badge uses light grey text on a light grey background. DevTools shows a contrast ratio of 2.1:1. This fails WCAG AA."
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
### Test 4 — Form Error Test
|
|
197
|
+
|
|
198
|
+
**What you are testing:** Are error messages helpful and correctly announced?
|
|
199
|
+
|
|
200
|
+
**How to do it:**
|
|
201
|
+
|
|
202
|
+
1. Find any form in the outcome you are testing.
|
|
203
|
+
2. Submit it empty (or with invalid values where the form requires specific formats).
|
|
204
|
+
3. Observe:
|
|
205
|
+
- Does an error message appear?
|
|
206
|
+
- Is it adjacent to the field that caused the error (not only at the top of the form)?
|
|
207
|
+
- Does the error message say specifically what is wrong? ("Enter a valid email address" not "Invalid input")
|
|
208
|
+
- Does the error message say how to fix it? ("Email addresses must include an @ symbol")
|
|
209
|
+
- Is the user's input preserved? (they should not have to re-type the whole form)
|
|
210
|
+
4. Fix one field and resubmit. Does the error for that field disappear while errors on other fields remain?
|
|
211
|
+
5. Fix all errors and submit. Does a clear success message appear?
|
|
212
|
+
|
|
213
|
+
**Pass criteria:**
|
|
214
|
+
- Error messages are specific and actionable
|
|
215
|
+
- Errors appear adjacent to the relevant field
|
|
216
|
+
- Input is preserved on error
|
|
217
|
+
- Success is clearly confirmed
|
|
218
|
+
|
|
219
|
+
**Fail example:** "When I submit the enrolment form without filling in the student's date of birth, a red banner appears at the top saying 'Please check your form'. There is no indication which field is wrong or what I need to enter. The form is cleared."
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
### Test 5 — Mobile Touch Target Test
|
|
224
|
+
|
|
225
|
+
**What you are testing:** Are interactive elements large enough to tap reliably on a touchscreen?
|
|
226
|
+
|
|
227
|
+
**How to do it:**
|
|
228
|
+
|
|
229
|
+
1. Open Chrome DevTools and switch to responsive mode (the device icon, or Cmd+Shift+M).
|
|
230
|
+
2. Set the viewport to 375px wide (iPhone SE).
|
|
231
|
+
3. For each interactive element on the page, visually estimate whether it is at least 44x44 pixels.
|
|
232
|
+
4. Pay particular attention to:
|
|
233
|
+
- Icon buttons (close, menu, edit, delete — these are commonly too small)
|
|
234
|
+
- Checkboxes and radio buttons
|
|
235
|
+
- Links within body copy
|
|
236
|
+
- Table action buttons that stack on mobile
|
|
237
|
+
|
|
238
|
+
To measure precisely: right-click on an element > Inspect. In DevTools, hover over the element in the Elements panel. The dimensions appear as a tooltip.
|
|
239
|
+
|
|
240
|
+
**Pass criteria:**
|
|
241
|
+
- All buttons, links, and form controls: minimum 44x44px touch target
|
|
242
|
+
- Spacing between targets: minimum 8px (targets should not be so close that an adjacent tap is easy to trigger accidentally)
|
|
243
|
+
|
|
244
|
+
**Fail example:** "On mobile, the delete button in the student list is an X icon that is 24x24 pixels. It is next to an edit icon also 24x24 pixels with 4px between them. Both fail the touch target requirement."
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Common Violations and Their Fixes
|
|
249
|
+
|
|
250
|
+
### Violation: Icon-only button with no accessible label
|
|
251
|
+
|
|
252
|
+
**What it looks like:** A button that is just a trash can icon, no text.
|
|
253
|
+
|
|
254
|
+
**Why it fails:** A screen reader reads it as "button" with no further description.
|
|
255
|
+
|
|
256
|
+
**Fix in shadcn/Tailwind:**
|
|
257
|
+
```tsx
|
|
258
|
+
<Button variant="ghost" size="icon" aria-label="Delete application from Sarah Chen">
|
|
259
|
+
<Trash2 className="h-4 w-4" />
|
|
260
|
+
</Button>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
### Violation: Form field with placeholder text but no label
|
|
266
|
+
|
|
267
|
+
**What it looks like:** An input that says "Enter your email" inside the field, with no label above it.
|
|
268
|
+
|
|
269
|
+
**Why it fails:** Placeholder text disappears when the user starts typing. Screen readers may not associate it correctly. Users with cognitive disabilities lose context mid-entry.
|
|
270
|
+
|
|
271
|
+
**Fix in shadcn/Tailwind:**
|
|
272
|
+
```tsx
|
|
273
|
+
<div className="space-y-2">
|
|
274
|
+
<Label htmlFor="email">Email address</Label>
|
|
275
|
+
<Input id="email" type="email" placeholder="name@example.com" />
|
|
276
|
+
</div>
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### Violation: Error message not connected to field
|
|
282
|
+
|
|
283
|
+
**What it looks like:** An error paragraph that appears near the form but is not semantically linked to the field it describes.
|
|
284
|
+
|
|
285
|
+
**Why it fails:** Screen readers cannot associate the error with the field.
|
|
286
|
+
|
|
287
|
+
**Fix:**
|
|
288
|
+
```tsx
|
|
289
|
+
<div className="space-y-2">
|
|
290
|
+
<Label htmlFor="email">Email address</Label>
|
|
291
|
+
<Input
|
|
292
|
+
id="email"
|
|
293
|
+
type="email"
|
|
294
|
+
aria-describedby="email-error"
|
|
295
|
+
aria-invalid={!!error}
|
|
296
|
+
/>
|
|
297
|
+
{error && (
|
|
298
|
+
<p id="email-error" className="text-sm text-destructive">
|
|
299
|
+
{error}
|
|
300
|
+
</p>
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### Violation: Colour as the only status indicator
|
|
308
|
+
|
|
309
|
+
**What it looks like:** A table where pending rows are grey, approved rows are green, and rejected rows are red — with no text labels.
|
|
310
|
+
|
|
311
|
+
**Why it fails:** Colour-blind users cannot distinguish red from green. Screen readers cannot convey colour.
|
|
312
|
+
|
|
313
|
+
**Fix:** Use shadcn `Badge` components with both colour and text:
|
|
314
|
+
```tsx
|
|
315
|
+
<Badge variant={status === 'approved' ? 'default' : status === 'rejected' ? 'destructive' : 'secondary'}>
|
|
316
|
+
{status.charAt(0).toUpperCase() + status.slice(1)}
|
|
317
|
+
</Badge>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### Violation: Focus not visible
|
|
323
|
+
|
|
324
|
+
**What it looks like:** Interactive elements that look the same whether focused or not.
|
|
325
|
+
|
|
326
|
+
**Why it fails:** Keyboard users cannot tell where they are on the page.
|
|
327
|
+
|
|
328
|
+
**Fix:** Never remove the `outline` without a replacement. shadcn components include `focus-visible:ring-2 focus-visible:ring-ring` by default. Do not remove these classes.
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## The Reduce Motion Preference
|
|
333
|
+
|
|
334
|
+
Some users configure their operating system to reduce motion because they experience nausea, dizziness, or disorientation from animated interfaces. This is common in people with vestibular disorders, migraines, or certain neurological conditions.
|
|
335
|
+
|
|
336
|
+
The operating system exposes this preference as a CSS media query: `prefers-reduced-motion: reduce`.
|
|
337
|
+
|
|
338
|
+
Framer Motion reads this preference through its `useReducedMotion` hook. Use it on any animation that involves movement (translate, scale, rotate). Opacity transitions (fade in/out) are generally safe to keep even when motion is reduced.
|
|
339
|
+
|
|
340
|
+
**Pattern to use in every animated component:**
|
|
341
|
+
```tsx
|
|
342
|
+
import { motion, useReducedMotion } from 'framer-motion';
|
|
343
|
+
|
|
344
|
+
function AnimatedList({ items }) {
|
|
345
|
+
const prefersReducedMotion = useReducedMotion();
|
|
346
|
+
|
|
347
|
+
const containerVariants = {
|
|
348
|
+
hidden: {},
|
|
349
|
+
visible: {
|
|
350
|
+
transition: {
|
|
351
|
+
staggerChildren: prefersReducedMotion ? 0 : 0.05,
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const itemVariants = {
|
|
357
|
+
hidden: {
|
|
358
|
+
opacity: 0,
|
|
359
|
+
y: prefersReducedMotion ? 0 : 8,
|
|
360
|
+
},
|
|
361
|
+
visible: {
|
|
362
|
+
opacity: 1,
|
|
363
|
+
y: 0,
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
return (
|
|
368
|
+
<motion.ul variants={containerVariants} initial="hidden" animate="visible">
|
|
369
|
+
{items.map(item => (
|
|
370
|
+
<motion.li key={item.id} variants={itemVariants}>
|
|
371
|
+
{item.content}
|
|
372
|
+
</motion.li>
|
|
373
|
+
))}
|
|
374
|
+
</motion.ul>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
When `prefersReducedMotion` is true, items still fade in (opacity transition) but do not slide (y transition is 0). The stagger is removed so all items appear simultaneously.
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Writing Accessibility Verification Steps into Outcome Documents
|
|
384
|
+
|
|
385
|
+
Every UI outcome should include accessibility verification steps alongside the standard walkthrough steps. These are not separate — they are part of the definition of done.
|
|
386
|
+
|
|
387
|
+
When writing or reviewing outcome documents, add this section after the walkthrough:
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
ACCESSIBILITY VERIFICATION
|
|
391
|
+
|
|
392
|
+
Keyboard:
|
|
393
|
+
- [ ] Tab from page entry through all interactive elements in logical order
|
|
394
|
+
- [ ] [Specific action] completable by keyboard alone
|
|
395
|
+
- [ ] All dialogs trap and return focus correctly
|
|
396
|
+
|
|
397
|
+
Screen reader:
|
|
398
|
+
- [ ] All form fields announce their labels
|
|
399
|
+
- [ ] [Specific button] announces "[expected announcement]"
|
|
400
|
+
- [ ] Success/error states announced automatically
|
|
401
|
+
|
|
402
|
+
Colour contrast:
|
|
403
|
+
- [ ] Body text passes 4.5:1 on all backgrounds in this view
|
|
404
|
+
- [ ] Status badges (Pending/Approved/Rejected) pass 4.5:1
|
|
405
|
+
|
|
406
|
+
Mobile:
|
|
407
|
+
- [ ] All touch targets 44px minimum at 375px viewport
|
|
408
|
+
- [ ] [Specific element] does not overflow on mobile
|
|
409
|
+
|
|
410
|
+
Errors:
|
|
411
|
+
- [ ] Error messages are specific and adjacent to the field
|
|
412
|
+
- [ ] User input is preserved when validation fails
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
These steps are run by the QA agent during the verification walkthrough and reported back in domain language.
|