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 +23 -7
- package/agents/picasso.md +305 -2
- package/package.json +1 -1
- package/skills/picasso/references/accessibility-wcag.md +245 -0
- package/skills/picasso/references/anti-patterns.md +138 -49
- package/skills/picasso/references/color-and-contrast.md +251 -2
- package/skills/picasso/references/conversion-design.md +193 -0
- package/skills/picasso/references/data-visualization.md +226 -0
- package/skills/picasso/references/modern-css-performance.md +361 -0
- package/skills/picasso/references/performance-optimization.md +746 -0
- package/skills/picasso/references/style-presets.md +502 -0
- package/skills/picasso/references/typography.md +206 -2
- package/skills/picasso/references/ux-psychology.md +235 -0
- package/skills/picasso/references/ux-writing.md +513 -0
- package/skills/picasso/references/accessibility.md +0 -172
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# UX Writing Reference
|
|
2
|
+
|
|
3
|
+
Rules and patterns for writing UI text. Every string a user reads is a design decision.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Button Labels
|
|
8
|
+
|
|
9
|
+
Never use generic labels. Every button tells the user exactly what will happen.
|
|
10
|
+
|
|
11
|
+
### Banned Labels
|
|
12
|
+
|
|
13
|
+
| Never Use | Why |
|
|
14
|
+
|-----------|-----|
|
|
15
|
+
| OK | Tells user nothing about the action |
|
|
16
|
+
| Submit | Vague, form-era holdover |
|
|
17
|
+
| Yes / No | Forces user to re-read the question |
|
|
18
|
+
| Click here | Accessibility failure, no meaning |
|
|
19
|
+
| Cancel (alone) | Ambiguous without paired action context |
|
|
20
|
+
|
|
21
|
+
### Pattern: Verb + Object
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Save changes
|
|
25
|
+
Create account
|
|
26
|
+
Delete 3 items
|
|
27
|
+
Export as PDF
|
|
28
|
+
Add to cart
|
|
29
|
+
Send invitation
|
|
30
|
+
Publish post
|
|
31
|
+
Archive conversation
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Destructive Actions: Name the Destruction + Show Count
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
// BAD
|
|
38
|
+
Are you sure? [Yes] [No]
|
|
39
|
+
|
|
40
|
+
// GOOD
|
|
41
|
+
Delete 14 messages permanently?
|
|
42
|
+
This cannot be undone.
|
|
43
|
+
[Keep messages] [Delete 14 messages]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
// BAD
|
|
48
|
+
Remove items? [OK] [Cancel]
|
|
49
|
+
|
|
50
|
+
// GOOD
|
|
51
|
+
Remove 3 items from your cart?
|
|
52
|
+
[Keep items] [Remove 3 items]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Paired Button Rules
|
|
56
|
+
|
|
57
|
+
| Scenario | Primary | Secondary |
|
|
58
|
+
|----------|---------|-----------|
|
|
59
|
+
| Save flow | Save changes | Discard |
|
|
60
|
+
| Creation | Create project | Cancel |
|
|
61
|
+
| Destructive | Keep items | Delete 5 items |
|
|
62
|
+
| Confirmation | Send invitation | Go back |
|
|
63
|
+
| Upload | Upload file | Choose different file |
|
|
64
|
+
|
|
65
|
+
The safe/non-destructive action is always visually more prominent (filled button). The destructive action is secondary (outlined or text-only).
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 2. Error Messages
|
|
70
|
+
|
|
71
|
+
### Formula: What Happened + Why + How to Fix
|
|
72
|
+
|
|
73
|
+
Every error message has three parts. Never skip "how to fix."
|
|
74
|
+
|
|
75
|
+
### Templates
|
|
76
|
+
|
|
77
|
+
**Format error:**
|
|
78
|
+
```
|
|
79
|
+
Title: Invalid email address
|
|
80
|
+
Body: Email addresses need an @ symbol and a domain (like name@example.com).
|
|
81
|
+
Action: [Fix email address]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Missing required field:**
|
|
85
|
+
```
|
|
86
|
+
Title: Card number is required
|
|
87
|
+
Body: Enter your 16-digit card number to complete checkout.
|
|
88
|
+
// Inline: highlight the field, place message directly below it
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Permission denied:**
|
|
92
|
+
```
|
|
93
|
+
Title: You don't have access to this project
|
|
94
|
+
Body: Only project admins can change billing settings. Ask [Admin Name] to update your role or make this change for you.
|
|
95
|
+
Action: [Request access] [Go back]
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Network error:**
|
|
99
|
+
```
|
|
100
|
+
Title: Can't connect right now
|
|
101
|
+
Body: Check your internet connection and try again. Your draft has been saved locally.
|
|
102
|
+
Action: [Try again]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Server error (5xx):**
|
|
106
|
+
```
|
|
107
|
+
Title: Something went wrong on our end
|
|
108
|
+
Body: We're looking into it. Your data is safe. Try again in a few minutes, or contact support if this keeps happening.
|
|
109
|
+
Action: [Try again] [Contact support]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Rate limit:**
|
|
113
|
+
```
|
|
114
|
+
Title: Too many requests
|
|
115
|
+
Body: You've hit the rate limit. Try again in [X] seconds.
|
|
116
|
+
Action: [auto-countdown button: "Try again in 28s"]
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Rules
|
|
120
|
+
|
|
121
|
+
- Never blame the user. "Invalid input" becomes "This field needs a number between 1 and 100."
|
|
122
|
+
- Never use error codes alone. "Error 403" means nothing. Always pair with human language.
|
|
123
|
+
- Never use "Oops!" or humor in error states. The user is blocked; respect that.
|
|
124
|
+
- Place errors inline next to the field, not in a modal or toast that disappears.
|
|
125
|
+
- Use red (#DC2626 or equivalent) for error borders/icons, not for the entire message text.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 3. Empty States
|
|
130
|
+
|
|
131
|
+
### Formula: Acknowledge + Explain Value + Action CTA + Optional Help
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
// Project list empty state
|
|
135
|
+
|
|
136
|
+
[illustration or icon]
|
|
137
|
+
|
|
138
|
+
No projects yet
|
|
139
|
+
Projects help you organize work, track progress, and collaborate with your team.
|
|
140
|
+
|
|
141
|
+
[Create your first project]
|
|
142
|
+
|
|
143
|
+
Need help getting started? Read the quick-start guide.
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
// Search with no results
|
|
148
|
+
|
|
149
|
+
No results for "exmple"
|
|
150
|
+
Did you mean "example"?
|
|
151
|
+
|
|
152
|
+
Try different keywords, remove filters, or [browse all items].
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
// Filtered view empty
|
|
157
|
+
|
|
158
|
+
No completed tasks this week
|
|
159
|
+
Completed tasks will appear here as your team finishes work.
|
|
160
|
+
|
|
161
|
+
[View all tasks]
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Rules
|
|
165
|
+
|
|
166
|
+
- Keep the acknowledgment to one short line. Don't over-explain emptiness.
|
|
167
|
+
- The value proposition should answer "why would I want things here?"
|
|
168
|
+
- The CTA button uses the same verb+object pattern as all buttons.
|
|
169
|
+
- Help links open in context (panel, tooltip, or new tab), never navigate away from the empty state.
|
|
170
|
+
- Never show a completely blank screen. Even loading-to-empty transitions need a state.
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 4. Voice vs Tone
|
|
175
|
+
|
|
176
|
+
**Voice** is the personality. It never changes. Define it once.
|
|
177
|
+
|
|
178
|
+
| Voice Attribute | Means | Does Not Mean |
|
|
179
|
+
|----------------|-------|---------------|
|
|
180
|
+
| Clear | Short sentences, common words | Dumbed down or robotic |
|
|
181
|
+
| Confident | Direct statements, no hedging | Arrogant or dismissive |
|
|
182
|
+
| Helpful | Guides next step, anticipates needs | Condescending or hand-holding |
|
|
183
|
+
| Human | Natural phrasing, contractions OK | Slang, jokes, or emoji in UI text |
|
|
184
|
+
|
|
185
|
+
**Tone** adapts to the moment.
|
|
186
|
+
|
|
187
|
+
| Moment | Tone | Example |
|
|
188
|
+
|--------|------|---------|
|
|
189
|
+
| Success | Warm, encouraging | "Your account is ready. Let's set up your first project." |
|
|
190
|
+
| Error | Calm, direct | "That file is too large. The maximum size is 25 MB." |
|
|
191
|
+
| Destructive confirmation | Serious, precise | "This will permanently delete 14 messages. This cannot be undone." |
|
|
192
|
+
| Onboarding | Friendly, guiding | "Welcome. We'll walk you through the basics in about 2 minutes." |
|
|
193
|
+
| Empty state | Neutral, inviting | "No notifications yet. You'll see updates from your team here." |
|
|
194
|
+
|
|
195
|
+
### Hard Rule
|
|
196
|
+
|
|
197
|
+
Never use humor in error states, destructive actions, or security messages. A user who just lost data does not want a witty quip.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## 5. Accessibility Writing
|
|
202
|
+
|
|
203
|
+
### Link Text
|
|
204
|
+
|
|
205
|
+
Links must make sense when read alone (screen readers navigate by link list).
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
// BAD
|
|
209
|
+
To learn more about pricing, click here.
|
|
210
|
+
|
|
211
|
+
// GOOD
|
|
212
|
+
View our pricing plans.
|
|
213
|
+
|
|
214
|
+
// BAD
|
|
215
|
+
Read more
|
|
216
|
+
|
|
217
|
+
// GOOD
|
|
218
|
+
Read the full accessibility guidelines
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Alt Text
|
|
222
|
+
|
|
223
|
+
Describe the information the image conveys, not the image itself.
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
// BAD
|
|
227
|
+
alt="photo"
|
|
228
|
+
alt="image of a chart"
|
|
229
|
+
alt="" (unless purely decorative)
|
|
230
|
+
|
|
231
|
+
// GOOD
|
|
232
|
+
alt="Revenue grew 34% from Q1 to Q4 2025"
|
|
233
|
+
alt="Team photo: 12 people standing in front of the office"
|
|
234
|
+
alt="" (decorative divider, background pattern)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Rule: If the image were deleted, what information would be lost? That is your alt text.
|
|
238
|
+
|
|
239
|
+
### Icon Buttons
|
|
240
|
+
|
|
241
|
+
Every icon-only button needs an `aria-label`.
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<!-- BAD -->
|
|
245
|
+
<button><TrashIcon /></button>
|
|
246
|
+
|
|
247
|
+
<!-- GOOD -->
|
|
248
|
+
<button aria-label="Delete message"><TrashIcon /></button>
|
|
249
|
+
|
|
250
|
+
<!-- With tooltip for sighted users too -->
|
|
251
|
+
<button aria-label="Delete message" title="Delete message">
|
|
252
|
+
<TrashIcon />
|
|
253
|
+
</button>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Form Labels
|
|
257
|
+
|
|
258
|
+
Every input has a visible `<label>`. Placeholder text is not a label.
|
|
259
|
+
|
|
260
|
+
```html
|
|
261
|
+
<!-- BAD -->
|
|
262
|
+
<input placeholder="Enter your email" />
|
|
263
|
+
|
|
264
|
+
<!-- GOOD -->
|
|
265
|
+
<label for="email">Email address</label>
|
|
266
|
+
<input id="email" placeholder="name@example.com" />
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## 6. Translation Planning
|
|
272
|
+
|
|
273
|
+
English is compact. Other languages expand. Design with this in mind.
|
|
274
|
+
|
|
275
|
+
### Expansion Percentages
|
|
276
|
+
|
|
277
|
+
| Language | Expansion | 10-char English becomes |
|
|
278
|
+
|----------|-----------|------------------------|
|
|
279
|
+
| German | +30% | ~13 characters |
|
|
280
|
+
| French | +20% | ~12 characters |
|
|
281
|
+
| Finnish | +30-40% | ~13-14 characters |
|
|
282
|
+
| Italian | +15% | ~11-12 characters |
|
|
283
|
+
| Spanish | +15-20% | ~12 characters |
|
|
284
|
+
| Portuguese | +20-25% | ~12-13 characters |
|
|
285
|
+
| Chinese | -30% | ~7 characters |
|
|
286
|
+
| Japanese | -20-30% | ~7-8 characters |
|
|
287
|
+
| Korean | -10-20% | ~8-9 characters |
|
|
288
|
+
| Arabic | +25% (RTL) | ~12-13 characters |
|
|
289
|
+
|
|
290
|
+
### Design Rules
|
|
291
|
+
|
|
292
|
+
- Buttons: Allow at least 30% extra width. Never set fixed-width buttons for text.
|
|
293
|
+
- Navigation: Horizontal nav bars that barely fit in English will break in German. Test with pseudolocalization.
|
|
294
|
+
- Truncation: If you must truncate, truncate with `...` and provide the full text on hover/focus via `title` attribute.
|
|
295
|
+
- Icons + text: Always pair icons with text labels for translatability. Icon-only works only for universally understood symbols (close X, hamburger menu).
|
|
296
|
+
- Strings: Never concatenate strings. Use full-sentence templates with placeholders: `"Showing {count} of {total} results"` not `"Showing " + count + " of " + total + " results"`.
|
|
297
|
+
- Pluralization: Use ICU MessageFormat or equivalent. Never assume `count === 1` is the only singular rule (Arabic has 6 plural forms).
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 7. Terminology Consistency
|
|
302
|
+
|
|
303
|
+
Pick one term. Use it everywhere. Document it here.
|
|
304
|
+
|
|
305
|
+
| Use This | Not This |
|
|
306
|
+
|----------|----------|
|
|
307
|
+
| Delete | Remove, Trash, Erase, Destroy |
|
|
308
|
+
| Settings | Preferences, Options, Configuration |
|
|
309
|
+
| Sign in | Log in, Login, Sign on |
|
|
310
|
+
| Sign out | Log out, Logout, Sign off |
|
|
311
|
+
| Sign up | Register, Create account, Join |
|
|
312
|
+
| Search | Find, Look up, Query |
|
|
313
|
+
| Edit | Modify, Change, Update (in UI labels) |
|
|
314
|
+
| Save | Apply (for saving data, not filter application) |
|
|
315
|
+
| Profile | Account (for user identity page) |
|
|
316
|
+
| Workspace | Organization, Team, Company |
|
|
317
|
+
| Member | User (in team context) |
|
|
318
|
+
| Notification | Alert (reserve "alert" for system-level) |
|
|
319
|
+
|
|
320
|
+
### Enforcement
|
|
321
|
+
|
|
322
|
+
- Create a glossary file in the project. Lint against it.
|
|
323
|
+
- When a new term is introduced, add it to the glossary before shipping.
|
|
324
|
+
- Search the entire codebase when renaming. Partial renames are worse than inconsistency.
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 8. Loading State Copy
|
|
329
|
+
|
|
330
|
+
Never show "Loading..." for more than 2 seconds without context.
|
|
331
|
+
|
|
332
|
+
### Progressive Messages
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
// Immediate (0-2s)
|
|
336
|
+
[spinner]
|
|
337
|
+
|
|
338
|
+
// Short wait (2-5s)
|
|
339
|
+
Loading your dashboard...
|
|
340
|
+
|
|
341
|
+
// Medium wait (5-15s)
|
|
342
|
+
Crunching the numbers. This usually takes a few seconds.
|
|
343
|
+
|
|
344
|
+
// Long wait (15s+)
|
|
345
|
+
Still working on it. Large datasets take a bit longer.
|
|
346
|
+
|
|
347
|
+
// Very long (30s+)
|
|
348
|
+
This is taking longer than usual. You can wait or [try again].
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Skeleton Screens Over Spinners
|
|
352
|
+
|
|
353
|
+
Use skeleton screens (gray placeholder shapes) for known layouts. Use spinners only for unknown or variable layouts.
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
// Known layout: use skeleton
|
|
357
|
+
[====== skeleton heading ======]
|
|
358
|
+
[== skeleton line 1 ===========]
|
|
359
|
+
[== skeleton line 2 =======]
|
|
360
|
+
[== skeleton line 3 ==============]
|
|
361
|
+
|
|
362
|
+
// Unknown layout: use spinner with context
|
|
363
|
+
[spinner] Searching 12,000 records...
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Progress Indicators
|
|
367
|
+
|
|
368
|
+
For operations with known progress, show a percentage or step count.
|
|
369
|
+
|
|
370
|
+
```
|
|
371
|
+
Uploading 3 of 7 files (42%)
|
|
372
|
+
[=========> ]
|
|
373
|
+
|
|
374
|
+
Importing contacts... Step 2 of 4: Validating email addresses
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## 9. Confirmation Dialogs
|
|
380
|
+
|
|
381
|
+
### Structure
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
Title: State what will happen (verb + object)
|
|
385
|
+
Body: Consequences in plain language. Irreversibility if applicable.
|
|
386
|
+
Actions: [Safe option] [Destructive option]
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Non-Destructive Confirmation
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
Title: Publish this article?
|
|
393
|
+
Body: It will be visible to everyone in your workspace immediately.
|
|
394
|
+
Actions: [Go back] [Publish article]
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### Destructive Confirmation
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
Title: Delete "Q4 Report" and 3 attachments?
|
|
401
|
+
Body: This will permanently delete the document and its attachments.
|
|
402
|
+
This cannot be undone.
|
|
403
|
+
Actions: [Keep document] [Delete document and attachments]
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### High-Stakes Destructive (Type-to-Confirm)
|
|
407
|
+
|
|
408
|
+
```
|
|
409
|
+
Title: Delete workspace "Acme Corp"?
|
|
410
|
+
Body: This will permanently delete the workspace, all 47 projects,
|
|
411
|
+
and all member data. This cannot be undone.
|
|
412
|
+
|
|
413
|
+
Type "Acme Corp" to confirm:
|
|
414
|
+
[ ]
|
|
415
|
+
|
|
416
|
+
Actions: [Cancel] [Delete workspace] (disabled until typed)
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Rules
|
|
420
|
+
|
|
421
|
+
- The safe action (cancel/keep/go back) is the visually prominent button (filled, primary color).
|
|
422
|
+
- The destructive action is secondary (outlined, red text or red outline).
|
|
423
|
+
- Never pre-select the destructive option.
|
|
424
|
+
- For keyboard users, focus lands on the safe action by default.
|
|
425
|
+
- Escape key always triggers the safe action.
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## 10. Microcopy
|
|
430
|
+
|
|
431
|
+
### Placeholder vs Label
|
|
432
|
+
|
|
433
|
+
Placeholders vanish on input. Labels stay visible. Use both.
|
|
434
|
+
|
|
435
|
+
```html
|
|
436
|
+
<!-- BAD: placeholder as only label -->
|
|
437
|
+
<input placeholder="Email" />
|
|
438
|
+
|
|
439
|
+
<!-- GOOD: visible label + helpful placeholder -->
|
|
440
|
+
<label for="email">Email address</label>
|
|
441
|
+
<input id="email" placeholder="name@example.com" />
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### Helper Text
|
|
445
|
+
|
|
446
|
+
Place below the input. Use for format hints, constraints, or context.
|
|
447
|
+
|
|
448
|
+
```
|
|
449
|
+
Password
|
|
450
|
+
[ ]
|
|
451
|
+
Must be at least 8 characters with one number and one symbol.
|
|
452
|
+
|
|
453
|
+
Phone number
|
|
454
|
+
[ ]
|
|
455
|
+
We'll only use this for two-factor authentication.
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Success Messages with Next Steps
|
|
459
|
+
|
|
460
|
+
Never dead-end a user after success. Always tell them what comes next.
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
// BAD
|
|
464
|
+
Success!
|
|
465
|
+
|
|
466
|
+
// GOOD
|
|
467
|
+
Account created. Check your email for a verification link.
|
|
468
|
+
|
|
469
|
+
// BAD
|
|
470
|
+
Payment received.
|
|
471
|
+
|
|
472
|
+
// GOOD
|
|
473
|
+
Payment received. Your order #4821 will ship within 2 business days.
|
|
474
|
+
You'll get a tracking email when it does. [View order details]
|
|
475
|
+
|
|
476
|
+
// BAD
|
|
477
|
+
File uploaded.
|
|
478
|
+
|
|
479
|
+
// GOOD
|
|
480
|
+
"Q4-Report.pdf" uploaded. [Share with team] [Upload another]
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Character Counts
|
|
484
|
+
|
|
485
|
+
Show remaining characters, not total limit, and only when the user is close to the limit.
|
|
486
|
+
|
|
487
|
+
```
|
|
488
|
+
// Show nothing until 80% of limit reached
|
|
489
|
+
// At 80%+:
|
|
490
|
+
23 characters remaining
|
|
491
|
+
|
|
492
|
+
// At limit:
|
|
493
|
+
0 characters remaining (input blocked or visual warning)
|
|
494
|
+
|
|
495
|
+
// Over limit (if allowed):
|
|
496
|
+
12 characters over limit (red, with explanation of what happens)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Toggle Labels
|
|
500
|
+
|
|
501
|
+
Label the current state, not the action. Or label both sides.
|
|
502
|
+
|
|
503
|
+
```
|
|
504
|
+
// BAD (ambiguous: is it on or will clicking turn it on?)
|
|
505
|
+
[toggle] Notifications
|
|
506
|
+
|
|
507
|
+
// GOOD: label both states
|
|
508
|
+
Notifications: Off [toggle] On
|
|
509
|
+
|
|
510
|
+
// GOOD: describe what toggle does
|
|
511
|
+
[toggle] Receive email notifications
|
|
512
|
+
Currently: Off
|
|
513
|
+
```
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
# Accessibility Reference
|
|
2
|
-
|
|
3
|
-
## Table of Contents
|
|
4
|
-
1. Semantic HTML
|
|
5
|
-
2. ARIA Patterns
|
|
6
|
-
3. Keyboard Navigation
|
|
7
|
-
4. Screen Reader Testing
|
|
8
|
-
5. Color and Contrast
|
|
9
|
-
6. Motion and Vestibular
|
|
10
|
-
7. Forms
|
|
11
|
-
8. Images and Media
|
|
12
|
-
9. Testing Tools
|
|
13
|
-
10. Common Mistakes
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
|
-
## 1. Semantic HTML
|
|
18
|
-
|
|
19
|
-
Use the correct HTML element for its purpose. A `<button>` is always better than a `<div onClick>`. Semantic elements provide free behavior: focus, activation, role announcement, and keyboard interaction.
|
|
20
|
-
|
|
21
|
-
### Landmark Regions
|
|
22
|
-
Every page needs landmarks (`<header>`, `<nav>`, `<main>`, `<aside>`, `<footer>`). Screen readers let users jump between them. Label multiples: `<nav aria-label="Primary">`.
|
|
23
|
-
|
|
24
|
-
### Document Outline
|
|
25
|
-
Heading levels (`h1`-`h6`) in order. Never skip levels. One `h1` per page.
|
|
26
|
-
|
|
27
|
-
### Element Selection
|
|
28
|
-
- Action: `<button>`. Navigation: `<a href>`. Lists: `<ul>`/`<ol>`. Data: `<table>` with `<th>`.
|
|
29
|
-
- Toggle: `<button aria-pressed>` or `<input type="checkbox">`. Expandable: `<details>`/`<summary>`.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## 2. ARIA Patterns
|
|
34
|
-
|
|
35
|
-
Do not use ARIA if native HTML achieves the same result. ARIA adds semantics but not behavior.
|
|
36
|
-
|
|
37
|
-
### Common Widgets
|
|
38
|
-
**Tabs**: `role="tablist"` container with `role="tab"` buttons. Arrow keys move between tabs, Tab moves into `role="tabpanel"`. Manage `aria-selected` and `hidden`.
|
|
39
|
-
|
|
40
|
-
**Dialog**: `role="dialog"` with `aria-modal="true"` and `aria-labelledby` pointing to the heading. Trap focus, close on Escape.
|
|
41
|
-
|
|
42
|
-
### Live Regions
|
|
43
|
-
Inject content into these containers; do not add the role after content exists.
|
|
44
|
-
```html
|
|
45
|
-
<div aria-live="polite" aria-atomic="true">3 items in cart</div>
|
|
46
|
-
<div role="alert">Payment failed.</div> <!-- assertive, interrupts -->
|
|
47
|
-
<div role="status">File uploaded.</div> <!-- polite, waits -->
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## 3. Keyboard Navigation
|
|
53
|
-
|
|
54
|
-
### Focus Indicators
|
|
55
|
-
Never remove outlines without replacement. Use `:focus-visible` for keyboard-only rings:
|
|
56
|
-
```css
|
|
57
|
-
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
### Tab Order
|
|
61
|
-
DOM order determines tab order. Never use positive `tabindex`. Use `tabindex="-1"` for programmatic focus only.
|
|
62
|
-
|
|
63
|
-
### Roving Tabindex
|
|
64
|
-
For composite widgets (toolbars, tab lists), one item is in the tab order at a time. Arrow keys move focus. Set `tabindex="0"` on the active item, `tabindex="-1"` on siblings, and call `.focus()` on arrow key press.
|
|
65
|
-
|
|
66
|
-
### Focus Trapping for Modals
|
|
67
|
-
On open: focus first focusable element. Tab/Shift+Tab cycles within. Escape closes. On close: return focus to trigger. Use `inert` on background content to prevent interaction outside the modal.
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
## 4. Screen Reader Testing
|
|
72
|
-
|
|
73
|
-
### VoiceOver (macOS)
|
|
74
|
-
Toggle: Cmd+F5. Navigate: VO (Ctrl+Option) + arrows. Rotor: VO+U. Test with Safari.
|
|
75
|
-
|
|
76
|
-
### NVDA (Windows)
|
|
77
|
-
Toggle: Ctrl+Alt+N. Elements list: NVDA+F7. Read all: NVDA+Down. Test with Firefox.
|
|
78
|
-
|
|
79
|
-
### Methodology
|
|
80
|
-
1. Navigate with SR only. Verify elements announce role, name, state.
|
|
81
|
-
2. Verify dynamic changes (toasts, errors) are announced via live regions.
|
|
82
|
-
3. Verify modals trap focus and announce title. Verify custom widget keyboard patterns.
|
|
83
|
-
|
|
84
|
-
### Gotchas
|
|
85
|
-
- `aria-hidden="true"` on a parent hides all children regardless of child attributes.
|
|
86
|
-
- Dynamic content is not announced unless in a live region or focus is moved to it.
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## 5. Color and Contrast
|
|
91
|
-
|
|
92
|
-
### WCAG 2.2 Requirements
|
|
93
|
-
Normal text: 4.5:1 AA, 7:1 AAA. Large text (>=24px / >=18.66px bold): 3:1 AA, 4.5:1 AAA. UI components and focus indicators: 3:1 AA. APCA provides a more perceptually accurate model and is expected in WCAG 3.0.
|
|
94
|
-
|
|
95
|
-
### Color Blindness
|
|
96
|
-
8% of men have color vision deficiency. Never rely on color alone. Pair red/green with icons. Use patterns in charts alongside color. See `color-and-contrast.md` for deeper guidance.
|
|
97
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
## 6. Motion and Vestibular
|
|
101
|
-
|
|
102
|
-
### prefers-reduced-motion
|
|
103
|
-
Remove translation, rotation, and scaling. Opacity fades remain acceptable.
|
|
104
|
-
```css
|
|
105
|
-
@media (prefers-reduced-motion: reduce) {
|
|
106
|
-
*, *::before, *::after {
|
|
107
|
-
animation-duration: 0.01ms !important;
|
|
108
|
-
transition-duration: 0.01ms !important;
|
|
109
|
-
scroll-behavior: auto !important;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### prefers-contrast
|
|
115
|
-
For `(prefers-contrast: more)`: bolder borders (2px+), higher text contrast, remove translucent/glass surfaces.
|
|
116
|
-
|
|
117
|
-
### What to Avoid
|
|
118
|
-
- Flashing faster than 3/second (WCAG 2.3.1), auto-playing large animations
|
|
119
|
-
- Parallax without reduced-motion fallback, infinite animations without pause
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## 7. Forms
|
|
124
|
-
|
|
125
|
-
Every input needs a programmatic `<label>`. Placeholders are not labels. Mark required fields with `required` and `aria-required="true"`.
|
|
126
|
-
|
|
127
|
-
For errors, use `aria-invalid="true"` and `aria-describedby` pointing to the error message:
|
|
128
|
-
```html
|
|
129
|
-
<input id="email" aria-invalid="true" aria-describedby="email-err" />
|
|
130
|
-
<p id="email-err" role="alert">Enter a valid email address.</p>
|
|
131
|
-
```
|
|
132
|
-
Validate on blur, not on keystroke. For multiple errors, move focus to an error summary with links to each invalid field.
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
## 8. Images and Media
|
|
137
|
-
|
|
138
|
-
### Alt Text
|
|
139
|
-
- **Informative**: Describe content and purpose. **Decorative**: `alt=""` (empty, not absent).
|
|
140
|
-
- **Functional** (in buttons/links): Describe the action, not the image. **Complex**: Short alt + `aria-describedby` or `<figcaption>`.
|
|
141
|
-
|
|
142
|
-
### Video and Audio
|
|
143
|
-
Captions for all video (WCAG 1.2.2). Audio descriptions for visual-only content (WCAG 1.2.5). Never auto-play audio. Media players must be keyboard-operable.
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
## 9. Testing Tools
|
|
148
|
-
|
|
149
|
-
### Automated
|
|
150
|
-
- **axe-core**: Industry standard. Browser extension, CLI, or CI via `@axe-core/playwright`. Catches ~30-40% of issues.
|
|
151
|
-
- **Lighthouse**: Chrome DevTools, uses axe-core. **pa11y**: CLI for CI, supports WCAG 2.2 AA/AAA.
|
|
152
|
-
|
|
153
|
-
### Manual Checklist
|
|
154
|
-
1. Tab through entire page. Logical order, every control reachable.
|
|
155
|
-
2. Activate controls with Enter and Space.
|
|
156
|
-
3. Modals: focus trapped, Escape closes, focus returns.
|
|
157
|
-
4. 400% zoom: no horizontal scroll (WCAG 1.4.10).
|
|
158
|
-
5. Screen reader: correct roles, names, and states.
|
|
159
|
-
6. Images have appropriate alt text. Forms have labels and announced errors.
|
|
160
|
-
|
|
161
|
-
---
|
|
162
|
-
|
|
163
|
-
## 10. Common Mistakes
|
|
164
|
-
|
|
165
|
-
- `<div>`/`<span>` for clickable elements instead of `<button>`/`<a>` (loses keyboard and semantics)
|
|
166
|
-
- `outline: none` without a visible replacement focus indicator
|
|
167
|
-
- Positive `tabindex` values (chaotic tab ordering)
|
|
168
|
-
- `aria-hidden="true"` on focusable elements (reachable but invisible to AT)
|
|
169
|
-
- `role="button"` on a `<div>` without `tabindex="0"` and Enter/Space handlers
|
|
170
|
-
- Missing `lang` attribute on `<html>` (screen readers guess language wrong)
|
|
171
|
-
- `aria-live` on a region updating every second (overwhelms SR users)
|
|
172
|
-
- Wrapping entire cards in `<a>` (SR reads all card text as the link name)
|