@hegemonart/get-design-done 1.16.0 → 1.19.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/marketplace.json +12 -4
- package/.claude-plugin/plugin.json +22 -4
- package/CHANGELOG.md +111 -0
- package/README.md +27 -2
- package/agents/design-auditor.md +65 -1
- package/agents/design-context-builder.md +6 -1
- package/agents/design-doc-writer.md +21 -0
- package/agents/design-executor.md +22 -4
- package/agents/design-pattern-mapper.md +62 -0
- package/agents/design-phase-researcher.md +1 -1
- package/agents/motion-mapper.md +74 -9
- package/agents/token-mapper.md +8 -0
- package/package.json +16 -2
- package/reference/components/README.md +27 -23
- package/reference/components/alert.md +198 -0
- package/reference/components/badge.md +202 -0
- package/reference/components/breadcrumbs.md +198 -0
- package/reference/components/chip.md +209 -0
- package/reference/components/command-palette.md +228 -0
- package/reference/components/date-picker.md +227 -0
- package/reference/components/file-upload.md +219 -0
- package/reference/components/list.md +217 -0
- package/reference/components/menu.md +212 -0
- package/reference/components/navbar.md +211 -0
- package/reference/components/pagination.md +205 -0
- package/reference/components/progress.md +210 -0
- package/reference/components/rich-text-editor.md +226 -0
- package/reference/components/sidebar.md +211 -0
- package/reference/components/skeleton.md +197 -0
- package/reference/components/slider.md +208 -0
- package/reference/components/stepper.md +220 -0
- package/reference/components/table.md +229 -0
- package/reference/components/toast.md +200 -0
- package/reference/components/tree.md +225 -0
- package/reference/css-grid-layout.md +835 -0
- package/reference/data-visualization.md +333 -0
- package/reference/external/NOTICE.hyperframes +28 -0
- package/reference/form-patterns.md +245 -0
- package/reference/image-optimization.md +582 -0
- package/reference/information-architecture.md +255 -0
- package/reference/motion-advanced.md +754 -0
- package/reference/motion-easings.md +381 -0
- package/reference/motion-interpolate.md +282 -0
- package/reference/motion-spring.md +234 -0
- package/reference/motion-transition-taxonomy.md +155 -0
- package/reference/motion.md +20 -0
- package/reference/onboarding-progressive-disclosure.md +250 -0
- package/reference/output-contracts/motion-map.schema.json +135 -0
- package/reference/platforms.md +346 -0
- package/reference/registry.json +445 -220
- package/reference/registry.schema.json +4 -0
- package/reference/rtl-cjk-cultural.md +353 -0
- package/reference/user-research.md +360 -0
- package/reference/variable-fonts-loading.md +532 -0
- package/scripts/lib/easings.cjs +280 -0
- package/scripts/lib/parse-contract.cjs +220 -0
- package/scripts/lib/spring.cjs +160 -0
- package/scripts/tests/test-motion-provenance.sh +64 -0
package/agents/motion-mapper.md
CHANGED
|
@@ -25,6 +25,10 @@ You inventory motion and animation patterns. Zero session memory. You do not mod
|
|
|
25
25
|
|
|
26
26
|
- `.design/STATE.md`
|
|
27
27
|
- `reference/motion.md` (if present)
|
|
28
|
+
- `reference/motion-advanced.md` (if present) — advanced patterns: spring physics, scroll-driven, FLIP, View Transitions API, gesture/drag mechanics, clip-path patterns, blur crossfades, Framer Motion hardware-accel gotcha
|
|
29
|
+
- `reference/motion-easings.md` (if present) — 12 canonical easing presets; classify each detected easing against this catalog
|
|
30
|
+
- `reference/motion-transition-taxonomy.md` (if present) — 8 transition families; classify page/route transitions against this taxonomy
|
|
31
|
+
- `reference/motion-spring.md` (if present) — spring presets; classify spring configs against gentle/wobbly/stiff/slow
|
|
28
32
|
- Any files supplied by the orchestrator
|
|
29
33
|
|
|
30
34
|
## Scan Strategy
|
|
@@ -61,8 +65,37 @@ From the collected values, bucket by:
|
|
|
61
65
|
- Normal: 200–400ms
|
|
62
66
|
- Slow: >400ms
|
|
63
67
|
|
|
68
|
+
## Advanced Scan Patterns (Phase 18+)
|
|
69
|
+
|
|
70
|
+
When `reference/motion-advanced.md` is present, additionally scan for:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Gesture / drag patterns
|
|
74
|
+
grep -rEn "setPointerCapture|onPointerDown.*drag|dragConstraints|useDragControls" src/ | head -40
|
|
75
|
+
grep -rEn "velocity|flick|swipe.*dismiss|drag.*dismiss" src/ | head -40
|
|
76
|
+
|
|
77
|
+
# Clip-path animations
|
|
78
|
+
grep -rEn "clip-path|clipPath|inset\(" src/ | head -40
|
|
79
|
+
|
|
80
|
+
# FLIP / View Transitions
|
|
81
|
+
grep -rEn "layoutId|startViewTransition|view-transition-name" src/ | head -30
|
|
82
|
+
|
|
83
|
+
# Scroll-driven
|
|
84
|
+
grep -rEn "animation-timeline|ScrollTimeline|useScroll\b" src/ | head -30
|
|
85
|
+
|
|
86
|
+
# WAAPI
|
|
87
|
+
grep -rEn "\.animate\(\[|WebAnimation|getAnimations" src/ | head -20
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Classify gesture patterns against `reference/motion-advanced.md` (velocity formula, pointer capture, multi-touch protection).
|
|
91
|
+
Classify easing values against the 12 canonical presets in `reference/motion-easings.md`; output `"custom"` with justification for anything that doesn't match.
|
|
92
|
+
Classify page/route transitions against the 8 families in `reference/motion-transition-taxonomy.md`.
|
|
93
|
+
Classify spring configs against the 4 presets in `reference/motion-spring.md`.
|
|
94
|
+
|
|
64
95
|
## Output Format — `.design/map/motion.md`
|
|
65
96
|
|
|
97
|
+
**The output MUST begin with a structured JSON block** enclosed in ` ```json ``` ` fences, followed by the prose sections. The JSON block must conform to `reference/output-contracts/motion-map.schema.json`. Malformed or missing blocks are validation failures.
|
|
98
|
+
|
|
66
99
|
```markdown
|
|
67
100
|
---
|
|
68
101
|
generated: [ISO 8601]
|
|
@@ -70,22 +103,54 @@ generated: [ISO 8601]
|
|
|
70
103
|
|
|
71
104
|
# Motion Map
|
|
72
105
|
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"schema_version": "1.0.0",
|
|
109
|
+
"generated_at": "[ISO 8601]",
|
|
110
|
+
"summary": {
|
|
111
|
+
"total_animations": 0,
|
|
112
|
+
"custom_easings": 0,
|
|
113
|
+
"reduced_motion_compliant": false,
|
|
114
|
+
"libraries": []
|
|
115
|
+
},
|
|
116
|
+
"animations": [
|
|
117
|
+
{
|
|
118
|
+
"id": "example-toast-enter",
|
|
119
|
+
"location": { "file": "src/components/Toast.tsx", "line": 12 },
|
|
120
|
+
"description": "Toast enter animation — opacity + translateY",
|
|
121
|
+
"easing": "cubic-out",
|
|
122
|
+
"duration_class": "quick",
|
|
123
|
+
"duration_ms": 180,
|
|
124
|
+
"trigger": "state-change",
|
|
125
|
+
"library": "framer-motion",
|
|
126
|
+
"reduced_motion_handled": true
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
73
132
|
## CSS transitions
|
|
74
|
-
| File | Property | Duration | Easing |
|
|
75
|
-
|
|
133
|
+
| File | Property | Duration | Easing | Canonical Easing |
|
|
134
|
+
|------|----------|----------|--------|-----------------|
|
|
76
135
|
|
|
77
136
|
## Library usage
|
|
78
137
|
| Library | Files | Notes |
|
|
79
138
|
|---------|-------|-------|
|
|
80
139
|
|
|
81
140
|
## Duration distribution
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
141
|
+
- Instant (<100ms): [N]
|
|
142
|
+
- Quick (100–200ms): [N]
|
|
143
|
+
- Standard (200–400ms): [N]
|
|
144
|
+
- Slow (400–800ms): [N]
|
|
145
|
+
- Narrative (>800ms): [N]
|
|
146
|
+
|
|
147
|
+
## Easing classification
|
|
148
|
+
| Detected Easing | Canonical Name | Count | Notes |
|
|
149
|
+
|----------------|---------------|-------|-------|
|
|
150
|
+
|
|
151
|
+
## Advanced patterns detected
|
|
152
|
+
| Pattern | Files | Notes |
|
|
153
|
+
|---------|-------|-------|
|
|
89
154
|
|
|
90
155
|
## Reduced-motion compliance
|
|
91
156
|
- `prefers-reduced-motion` queries present: [N]
|
package/agents/token-mapper.md
CHANGED
|
@@ -119,6 +119,14 @@ After the standard token inventory, scan and report:
|
|
|
119
119
|
- Check if they have `font-variant-numeric: tabular-nums` or Tailwind `tabular-nums` class
|
|
120
120
|
- Report missing instances
|
|
121
121
|
|
|
122
|
+
5. **Easing token consolidation** (when `reference/motion-easings.md` is present)
|
|
123
|
+
- Grep: `cubic-bezier\(` in CSS/SCSS/styled-components/Tailwind config
|
|
124
|
+
- Grep: `ease:` or `easing:` in Framer Motion / GSAP configs
|
|
125
|
+
- For each raw easing value found, check if a canonical `--ease-*` token from `reference/motion-easings.md` would serve the same purpose
|
|
126
|
+
- Report raw values that map to a canonical preset (recommend the `--ease-*` token)
|
|
127
|
+
- Report raw values with no canonical match as informational (may warrant a custom token)
|
|
128
|
+
- Do NOT flag values that are already using `--ease-*` tokens
|
|
129
|
+
|
|
122
130
|
### Output format:
|
|
123
131
|
```
|
|
124
132
|
## Micro-polish token findings
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hegemonart/get-design-done",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "A Claude Code plugin for systematic design improvement",
|
|
5
5
|
"author": "Hegemon",
|
|
6
6
|
"homepage": "https://github.com/hegemonart/get-design-done",
|
|
@@ -67,8 +67,22 @@
|
|
|
67
67
|
"style-vocabulary",
|
|
68
68
|
"industry-palettes",
|
|
69
69
|
"ui-style-vocabulary",
|
|
70
|
+
"variable-fonts",
|
|
71
|
+
"container-queries",
|
|
72
|
+
"view-transitions",
|
|
73
|
+
"motion-vocabulary",
|
|
74
|
+
"motion-easings",
|
|
75
|
+
"transition-taxonomy",
|
|
76
|
+
"gesture-mechanics",
|
|
77
|
+
"clip-path-animation",
|
|
70
78
|
"component-specs",
|
|
71
|
-
"design-system-benchmarks"
|
|
79
|
+
"design-system-benchmarks",
|
|
80
|
+
"i18n",
|
|
81
|
+
"user-research",
|
|
82
|
+
"information-architecture",
|
|
83
|
+
"form-patterns",
|
|
84
|
+
"data-viz",
|
|
85
|
+
"platforms"
|
|
72
86
|
],
|
|
73
87
|
"skills": [
|
|
74
88
|
"SKILL.md"
|
|
@@ -41,40 +41,44 @@ Typography · Keyboard/a11y · Motion · Do/Don't · Anti-patterns · Citations
|
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
## Wave 3 — Feedback
|
|
44
|
+
## Wave 3 — Feedback
|
|
45
45
|
|
|
46
46
|
| Component | Spec | Purpose |
|
|
47
47
|
|-----------|------|---------|
|
|
48
|
-
| Toast / Snackbar |
|
|
49
|
-
| Alert / Banner |
|
|
50
|
-
| Progress |
|
|
51
|
-
| Skeleton |
|
|
52
|
-
| Badge |
|
|
53
|
-
| Chip / Tag |
|
|
48
|
+
| Toast / Snackbar | [toast.md](toast.md) | Ephemeral status notification; auto-dismisses 4–8s |
|
|
49
|
+
| Alert / Banner | [alert.md](alert.md) | Persistent inline status message; four severity variants |
|
|
50
|
+
| Progress | [progress.md](progress.md) | Linear and circular completion indicator; determinate + indeterminate |
|
|
51
|
+
| Skeleton | [skeleton.md](skeleton.md) | Loading placeholder matching content shape |
|
|
52
|
+
| Badge | [badge.md](badge.md) | Numeric or status indicator overlaid on another element |
|
|
53
|
+
| Chip / Tag | [chip.md](chip.md) | Compact toggleable/removable label; filter, input, suggestion, display |
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
## Wave 4 — Navigation *(
|
|
57
|
+
## Wave 4 — Navigation & Data *(v1.17.0 · plan 17-02)*
|
|
58
58
|
|
|
59
59
|
| Component | Spec | Purpose |
|
|
60
60
|
|-----------|------|---------|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
61
|
+
| Menu | [menu.md](menu.md) | Dropdown and context menu; action list with ARIA menu roles |
|
|
62
|
+
| Navbar | [navbar.md](navbar.md) | Top navigation bar; primary nav + skip link + mobile hamburger |
|
|
63
|
+
| Sidebar | [sidebar.md](sidebar.md) | Collapsible side navigation panel; icon+label / icon-only states |
|
|
64
|
+
| Breadcrumbs | [breadcrumbs.md](breadcrumbs.md) | Hierarchical location trail; aria-current + hidden separators |
|
|
65
|
+
| Pagination | [pagination.md](pagination.md) | Page-set navigation; previous/next + page buttons + per-page |
|
|
66
|
+
| Table | [table.md](table.md) | Data table with sorting, selection, sticky header, virtualisation |
|
|
67
|
+
| List | [list.md](list.md) | Display list (`<ul>/<ol>`) and interactive listbox (`role="listbox"`) |
|
|
68
|
+
| Tree | [tree.md](tree.md) | Hierarchical tree view; expand/collapse with full ARIA tree roles |
|
|
69
|
+
| Command Palette | [command-palette.md](command-palette.md) | Global Cmd/Ctrl+K launcher; dialog + combobox + listbox pattern |
|
|
66
70
|
|
|
67
71
|
---
|
|
68
72
|
|
|
69
|
-
## Wave 5 — Data & Advanced
|
|
73
|
+
## Wave 5 — Data & Advanced
|
|
70
74
|
|
|
71
75
|
| Component | Spec | Purpose |
|
|
72
76
|
|-----------|------|---------|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
| File Upload |
|
|
76
|
-
| Rich
|
|
77
|
-
|
|
|
77
|
+
| Date Picker | [date-picker.md](date-picker.md) | Calendar-based date/range selection; input + popover variants |
|
|
78
|
+
| Slider | [slider.md](slider.md) | Single-value and range thumb on a track; full keyboard contract |
|
|
79
|
+
| File Upload | [file-upload.md](file-upload.md) | Drag-drop zone + accessible file input; per-file progress list |
|
|
80
|
+
| Rich-Text Editor | [rich-text-editor.md](rich-text-editor.md) | WYSIWYG authoring with contenteditable + toolbar + mentions |
|
|
81
|
+
| Stepper / Wizard | [stepper.md](stepper.md) | Sequential multi-step flow; role="list" (not tablist) |
|
|
78
82
|
|
|
79
83
|
---
|
|
80
84
|
|
|
@@ -84,7 +88,7 @@ Typography · Keyboard/a11y · Motion · Do/Don't · Anti-patterns · Citations
|
|
|
84
88
|
|------|-------|--------|
|
|
85
89
|
| Wave 1 — Inputs | 8 | v1.16.0 |
|
|
86
90
|
| Wave 2 — Containers | 7 | v1.16.0 |
|
|
87
|
-
| Wave 3 — Feedback | 6 |
|
|
88
|
-
| Wave 4 — Navigation |
|
|
89
|
-
| Wave 5 — Data & Advanced | 5 |
|
|
90
|
-
| **Total** | **
|
|
91
|
+
| Wave 3 — Feedback | 6 | v1.17.0 |
|
|
92
|
+
| Wave 4 — Navigation & Data | 9 | v1.17.0 |
|
|
93
|
+
| Wave 5 — Data & Advanced | 5 | v1.17.0 |
|
|
94
|
+
| **Total** | **35** | — |
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Alert / Banner — Benchmark Spec
|
|
2
|
+
|
|
3
|
+
**Harvested from**: Material 3 (Banner), Carbon (InlineNotification), Polaris (Banner), Atlassian (SectionMessage)
|
|
4
|
+
**Wave**: 3 · **Category**: Feedback
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
An alert (inline notification or banner) is a persistent in-page message that communicates status, warnings, or errors relevant to the current context. Unlike Toast, it does not auto-dismiss — it remains visible until the user dismisses it or the underlying condition resolves. Use alert for messages that require acknowledgement or that must remain visible for reference. *(Material 3, Carbon, Polaris, Atlassian agree: alert = persistent inline feedback)*
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Anatomy
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
┌─────────────────────────────────────────────────────────┐
|
|
18
|
+
│ [icon] [Title (optional)] [✕ dismiss?]│
|
|
19
|
+
│ Message text │
|
|
20
|
+
│ [Primary action?] [Secondary action?] │
|
|
21
|
+
└─────────────────────────────────────────────────────────┘
|
|
22
|
+
↑ role="alert" (error/warning) or role="status" (info/success)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
| Part | Required | Notes |
|
|
26
|
+
|------|----------|-------|
|
|
27
|
+
| Container | Yes | `role="alert"` or `role="status"` per severity |
|
|
28
|
+
| Severity icon | Yes | Visual + semantic variant indicator; never color alone |
|
|
29
|
+
| Message text | Yes | Concise description of status or error |
|
|
30
|
+
| Title | No | Recommended for error/warning; provides quick scanning |
|
|
31
|
+
| Primary action | No | One CTA linking to resolution (e.g. "Retry", "Review") |
|
|
32
|
+
| Secondary action | No | Supplemental link; lower emphasis |
|
|
33
|
+
| Dismiss button | No | Required when alert can be resolved by user |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Variants
|
|
38
|
+
|
|
39
|
+
| Variant | Description | Systems |
|
|
40
|
+
|---------|-------------|---------|
|
|
41
|
+
| Info | Blue; neutral informational message; `role="status"` polite | All |
|
|
42
|
+
| Success | Green; completed action confirmation; `role="status"` polite | All |
|
|
43
|
+
| Warning | Amber; potentially harmful condition; `role="alert"` assertive | All |
|
|
44
|
+
| Error | Red; failure or blocking condition; `role="alert"` assertive | All |
|
|
45
|
+
|
|
46
|
+
**Norm** (≥4/18 systems agree): four-variant semantic model (info/success/warning/error) with matching color, icon, and role. Icon is REQUIRED — color alone violates WCAG 1.4.1.
|
|
47
|
+
**Diverge**: Atlassian names this "SectionMessage" and omits dismiss; Carbon always includes an X; Polaris supports titled and untitled variants; Material 3 "Banner" supports one CTA only.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## States
|
|
52
|
+
|
|
53
|
+
| State | Trigger | Visual | ARIA |
|
|
54
|
+
|-------|---------|--------|------|
|
|
55
|
+
| visible | render | Full opacity, inline position | `role="alert"` or `role="status"` |
|
|
56
|
+
| hover | pointer over action | Action underline / background change | — |
|
|
57
|
+
| focus | keyboard on action/dismiss | focus-visible ring on button | — |
|
|
58
|
+
| dismissed | click dismiss button | Collapsed / removed | Removed from DOM |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Sizing & Spacing
|
|
63
|
+
|
|
64
|
+
| Placement | Width | Radius | Padding |
|
|
65
|
+
|-----------|-------|--------|---------|
|
|
66
|
+
| Full-width (page/section) | 100% of container | 0px | 16px 20px |
|
|
67
|
+
| Inline / card-contained | fit-to-container | 6–8px | 12px 16px |
|
|
68
|
+
|
|
69
|
+
| Property | Value | Notes |
|
|
70
|
+
|----------|-------|-------|
|
|
71
|
+
| Icon size | 20px | Aligned to first line of text |
|
|
72
|
+
| Gap (icon → text) | 12px | |
|
|
73
|
+
| Min height | 48px single-line | Grows with content |
|
|
74
|
+
|
|
75
|
+
**Norm**: Full-width placement in page-level contexts; rounded corners for component-level inline placement *(Material 3, Carbon, Atlassian)*.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Typography
|
|
80
|
+
|
|
81
|
+
- Title: label-md (14px/600) — gives quick scannable context for complex errors
|
|
82
|
+
- Message: body-sm (14px/400) — readable at inline scale
|
|
83
|
+
- Action: label-sm (13px/500) — matches action button label weight
|
|
84
|
+
|
|
85
|
+
Cross-link: `reference/typography.md` — body-sm, label-md definitions
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Keyboard & Accessibility
|
|
90
|
+
|
|
91
|
+
> **WAI-ARIA role**: `alert` (error/warning) or `status` (info/success)
|
|
92
|
+
> **Required attributes**: `role` on container; icon must have `aria-hidden="true"` + text-based variant reinforcement
|
|
93
|
+
|
|
94
|
+
### Keyboard Contract
|
|
95
|
+
|
|
96
|
+
*Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/patterns/alert/ — W3C — 2024*
|
|
97
|
+
|
|
98
|
+
| Key | Action |
|
|
99
|
+
|-----|--------|
|
|
100
|
+
| Tab | Moves focus to action buttons or dismiss button in document order |
|
|
101
|
+
| Enter / Space | Activates focused action or dismiss button |
|
|
102
|
+
| Escape | No special behavior (unlike modal); alert stays visible |
|
|
103
|
+
|
|
104
|
+
The alert container itself is not focusable. Child buttons follow standard button keyboard contract.
|
|
105
|
+
|
|
106
|
+
### Accessibility Rules
|
|
107
|
+
|
|
108
|
+
- Icon MUST be `aria-hidden="true"` — the icon is decorative reinforcement; the `role` and text carry the semantic meaning *(WCAG 1.4.1)*
|
|
109
|
+
- Color MUST NOT be the sole differentiator between variants — icon shape and text label must also differ *(WCAG 1.4.1)*
|
|
110
|
+
- `role="alert"` causes immediate assertion by screen readers — only use for error and warning severity
|
|
111
|
+
- `role="status"` uses a polite live region — appropriate for info and success
|
|
112
|
+
- Dismiss button MUST have `aria-label` (e.g. `aria-label="Dismiss warning"`) *(WAI-ARIA APG)*
|
|
113
|
+
- Do NOT auto-dismiss alerts — that is Toast's job; alert stays until explicitly dismissed *(Carbon, Polaris)*
|
|
114
|
+
|
|
115
|
+
Cross-link: `reference/accessibility.md` — live-regions, color-contrast sections
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Motion
|
|
120
|
+
|
|
121
|
+
| Transition | Duration | Easing | Notes |
|
|
122
|
+
|------------|----------|--------|-------|
|
|
123
|
+
| Enter (height expand + fade) | 200ms | ease-out | Pushes content down; avoids position:fixed |
|
|
124
|
+
| Exit (height collapse + fade) | 150ms | ease-in | Content reflows up as alert closes |
|
|
125
|
+
| Icon entrance | 0ms | — | No animation on icon; immediate |
|
|
126
|
+
|
|
127
|
+
**BAN**: Slide-in from edge (reserve for Toast). Alert is inline — it should feel like content appearing, not a notification arriving.
|
|
128
|
+
|
|
129
|
+
Cross-link: `reference/motion.md` — `prefers-reduced-motion`: skip height animation, instant show/hide
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Do / Don't
|
|
134
|
+
|
|
135
|
+
### Do
|
|
136
|
+
- Always include an icon matching the semantic variant *(WCAG 1.4.1 — color not sole differentiator)*
|
|
137
|
+
- Use `role="alert"` for warning/error (assertive) and `role="status"` for info/success (polite) *(WAI-ARIA APG)*
|
|
138
|
+
- Keep alert visible until the condition is resolved or user dismisses *(Carbon, Polaris)*
|
|
139
|
+
- Use full-width for page-level alerts; rounded inline for component-level *(Material 3, Carbon)*
|
|
140
|
+
|
|
141
|
+
### Don't
|
|
142
|
+
- Don't auto-dismiss alerts — use Toast for transient messages *(Material 3, Carbon)*
|
|
143
|
+
- Don't use color alone to distinguish severity — always include an icon and text label *(WCAG 1.4.1)*
|
|
144
|
+
- Don't stack more than 2 alerts in the same section — consolidate into a single summary *(Polaris)*
|
|
145
|
+
- Don't use `role="alert"` for info/success — overly assertive announcements desensitize users *(WAI-ARIA APG)*
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Anti-patterns Cross-links
|
|
150
|
+
|
|
151
|
+
| Anti-pattern | Entry |
|
|
152
|
+
|--------------|-------|
|
|
153
|
+
| Color as sole variant differentiator | `reference/anti-patterns.md#ban-color-only` |
|
|
154
|
+
| role="alert" on info/success (over-announcing) | `reference/anti-patterns.md#ban-live-region` |
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Benchmark Citations
|
|
159
|
+
|
|
160
|
+
| Claim | Sources |
|
|
161
|
+
|-------|---------|
|
|
162
|
+
| Icon required; color not sole differentiator | WCAG 1.4.1; Material 3, Carbon, Polaris, Atlassian |
|
|
163
|
+
| role="alert" for error/warning; role="status" for info/success | WAI-ARIA APG, Carbon, Polaris |
|
|
164
|
+
| No auto-dismiss | Material 3, Carbon, Polaris, Atlassian |
|
|
165
|
+
| Full-width vs. rounded inline placement | Material 3, Carbon, Atlassian |
|
|
166
|
+
| Dismiss button requires aria-label | WAI-ARIA APG |
|
|
167
|
+
|
|
168
|
+
Full system URLs: `connections/design-corpora.md`
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Grep Signatures
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Alert missing role attribute entirely
|
|
176
|
+
grep -rn 'alert\|Alert\|banner\|Banner\|notification' src/ | grep 'class=\|className=' | grep -v 'role='
|
|
177
|
+
|
|
178
|
+
# Error/warning alert using role="status" instead of role="alert"
|
|
179
|
+
grep -rn 'role="status"' src/ | grep -i 'error\|warning\|danger'
|
|
180
|
+
|
|
181
|
+
# Color-only differentiation — severity class with no icon reference
|
|
182
|
+
grep -rn 'alert--error\|alert--warning\|alert-error\|alert-warning' src/ | grep -v 'icon\|Icon\|svg\|SVG'
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Failing Example
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<!-- BAD: alert using only color class — no icon, no role, no text variant label -->
|
|
191
|
+
<div class="alert alert--error">
|
|
192
|
+
Your session has expired. Please log in again.
|
|
193
|
+
</div>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Why it fails**: No `role="alert"` so screen readers do not announce the error message. Color class alone distinguishes severity — users with color blindness or high-contrast themes cannot distinguish this from a neutral message. No icon provides a shape-based cue.
|
|
197
|
+
**Grep detection**: `grep -rn 'class.*alert--error\|class.*alert-error' src/ | grep -v 'role='`
|
|
198
|
+
**Fix**: Add `role="alert"`, include an error icon with `aria-hidden="true"`, and ensure the variant is communicated through text or visually-hidden label in addition to color.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Badge — Benchmark Spec
|
|
2
|
+
|
|
3
|
+
**Harvested from**: Material 3, Polaris, Carbon, Radix
|
|
4
|
+
**Wave**: 3 · **Category**: Feedback
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
A badge is a compact numeric counter or status indicator overlaid on or beside a parent element (icon, avatar, button) to communicate a count (unread messages, notification count) or status (online, offline, busy). It is purely decorative — the accessible count or status is surfaced through the parent element's `aria-label`. *(Material 3, Polaris, Carbon, Radix agree: badge is decorative; parent carries accessible state)*
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Anatomy
|
|
15
|
+
|
|
16
|
+
**Attached (overlay)**
|
|
17
|
+
```
|
|
18
|
+
┌──────────────────────┐
|
|
19
|
+
│ [icon/avatar] │
|
|
20
|
+
│ ┌──┐│
|
|
21
|
+
│ │ 3││ ← badge, position: absolute top-right
|
|
22
|
+
│ └──┘│
|
|
23
|
+
└──────────────────────┘
|
|
24
|
+
↑ parent: aria-label="Messages, 3 unread"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Standalone**
|
|
28
|
+
```
|
|
29
|
+
┌──────┐
|
|
30
|
+
│ 99+ │ ← badge standalone (rare; decorative in a list)
|
|
31
|
+
└──────┘
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Part | Required | Notes |
|
|
35
|
+
|------|----------|-------|
|
|
36
|
+
| Badge element | Yes | Pill or circle shape containing count or dot |
|
|
37
|
+
| Count / label text | Conditional | Present for count/icon variants; absent for dot |
|
|
38
|
+
| Parent element | Yes (for attached) | Carries `aria-label` with accessible count |
|
|
39
|
+
| Dot indicator | No | Status dot variant — no number, pure color/shape |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Variants
|
|
44
|
+
|
|
45
|
+
| Variant | Description | Systems |
|
|
46
|
+
|---------|-------------|---------|
|
|
47
|
+
| Count | Numeric; shows integer up to 99, then "99+" | Material 3, Polaris, Carbon, Radix |
|
|
48
|
+
| Dot | Status indicator; no number; color/position only | Material 3, Polaris, Radix |
|
|
49
|
+
| Icon overlay | Small icon inside badge shape (rare) | Material 3 |
|
|
50
|
+
|
|
51
|
+
**Norm** (≥4/18 systems agree): count badges cap display at "99+"; zero count hidden by default; dot variant uses the same position/size slot as count but without text.
|
|
52
|
+
**Diverge**: Material 3 calls the dot variant "small badge" (8px, no content) vs. "large badge" (16px+, with number); Polaris uses "badge" exclusively for text status labels (not overlaid); Carbon uses "tag" for text labels, "notification badge" for counts. Radix provides a headless primitive suitable for all variants.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## States
|
|
57
|
+
|
|
58
|
+
| State | Trigger | Visual | ARIA |
|
|
59
|
+
|-------|---------|--------|------|
|
|
60
|
+
| default | count > 0 | Badge visible, pill shape | Parent `aria-label` updated |
|
|
61
|
+
| zero | count = 0 | Badge hidden (default); visible if `showZero` prop | Parent `aria-label` reflects 0 or omits count |
|
|
62
|
+
| max overflow | count > 99 | Renders "99+" | Parent `aria-label` says "99 or more unread" |
|
|
63
|
+
| dot status | status active | Dot visible; colored by status | Parent `aria-label` includes status text |
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Sizing & Spacing
|
|
68
|
+
|
|
69
|
+
| Variant | Min height | Min width | Font size | Notes |
|
|
70
|
+
|---------|-----------|-----------|-----------|-------|
|
|
71
|
+
| Count (sm) | 16px | 16px (= height) | 10px/500 | Single digit fills circle |
|
|
72
|
+
| Count (md) | 20px | 20px (= height) | 12px/500 | Two-digit + "99+" |
|
|
73
|
+
| Dot | 8px | 8px | — | No text; absolute top-right |
|
|
74
|
+
|
|
75
|
+
- **Shape**: pill when width > height; circle when width = height (single digit)
|
|
76
|
+
- **Position** (attached): `position: absolute; top: -4px; right: -4px` relative to parent
|
|
77
|
+
- **Min-width = height** (pill rule): ensures pill does not compress on 2-digit counts
|
|
78
|
+
|
|
79
|
+
**Norm**: 16–20px height; 10–12px font-size; min-width equals height *(Material 3, Polaris, Carbon)*.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Typography
|
|
84
|
+
|
|
85
|
+
- Count text: 10–12px / 500 (medium weight) — legible at small scale
|
|
86
|
+
- No wrapping; never truncate count text; use "99+" cap instead
|
|
87
|
+
- Letter-spacing: 0 — tight spacing at 10–12px scale
|
|
88
|
+
|
|
89
|
+
Cross-link: `reference/typography.md` — small label scale (10–12px)
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Keyboard & Accessibility
|
|
94
|
+
|
|
95
|
+
> **WAI-ARIA role**: none (decorative); parent element carries accessible count via `aria-label`
|
|
96
|
+
> **Required attributes**: none on badge itself; `aria-label` on parent with count embedded
|
|
97
|
+
|
|
98
|
+
### Keyboard Contract
|
|
99
|
+
|
|
100
|
+
*Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/ — W3C — 2024*
|
|
101
|
+
|
|
102
|
+
| Key | Action |
|
|
103
|
+
|-----|--------|
|
|
104
|
+
| (none) | Badge is non-interactive; no keyboard behavior |
|
|
105
|
+
|
|
106
|
+
Badge never receives focus. It is a purely visual overlay. All keyboard interaction is on the parent element.
|
|
107
|
+
|
|
108
|
+
### Accessibility Rules
|
|
109
|
+
|
|
110
|
+
- Badge element itself MUST have `aria-hidden="true"` — screen readers should not announce the badge number separately; they read it as part of the parent's `aria-label`
|
|
111
|
+
- Parent element MUST have an `aria-label` that includes the count: `aria-label="Messages, 3 unread"` *(WAI-ARIA APG, Material 3)*
|
|
112
|
+
- Parent `aria-label` MUST be updated dynamically when count changes — use `aria-live` on the parent if the count changes while the page is rendered *(WCAG 4.1.3)*
|
|
113
|
+
- Zero badge: if badge is hidden at zero, remove it from DOM (or `display: none`) — do NOT leave it with empty text in the DOM *(Polaris)*
|
|
114
|
+
- Dot variant: parent `aria-label` MUST include the status: `aria-label="User profile, status: online"` *(Radix)*
|
|
115
|
+
- Never rely on badge color alone to communicate status — include text in parent `aria-label` *(WCAG 1.4.1)*
|
|
116
|
+
|
|
117
|
+
Cross-link: `reference/accessibility.md` — dynamic label updates, aria-live
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Motion
|
|
122
|
+
|
|
123
|
+
| Transition | Duration | Easing | Notes |
|
|
124
|
+
|------------|----------|--------|-------|
|
|
125
|
+
| Count increment (scale pulse) | 150ms | ease-out | Brief scale 1.2 → 1.0 on value change |
|
|
126
|
+
| Badge appear | 100ms | ease-out | Fade + scale from 0.5 |
|
|
127
|
+
| Badge disappear (at zero) | 80ms | ease-in | Fade + scale to 0 |
|
|
128
|
+
|
|
129
|
+
**BAN**: Continuous bounce animation on badge — implies urgency and is distracting; one-shot pulse on value change is acceptable.
|
|
130
|
+
|
|
131
|
+
Cross-link: `reference/motion.md` — scale-in/scale-out; `prefers-reduced-motion`: skip all badge animation
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Do / Don't
|
|
136
|
+
|
|
137
|
+
### Do
|
|
138
|
+
- Include count in parent `aria-label`: `aria-label="Inbox, 5 unread messages"` *(WAI-ARIA APG)*
|
|
139
|
+
- Add `aria-hidden="true"` to the badge element itself *(WAI-ARIA APG)*
|
|
140
|
+
- Cap numeric display at "99+" and update parent label to "99 or more" *(Material 3, Carbon)*
|
|
141
|
+
- Hide badge when count is 0 by default; offer `showZero` prop for product preference *(Polaris, Radix)*
|
|
142
|
+
|
|
143
|
+
### Don't
|
|
144
|
+
- Don't surface badge count only through color (dot badge without parent label) *(WCAG 1.4.1)*
|
|
145
|
+
- Don't put interactive content in a badge — it is always decorative *(Material 3, Carbon)*
|
|
146
|
+
- Don't animate badge continuously — one-shot pulse on value change only *(Polaris)*
|
|
147
|
+
- Don't use badge text as the only accessible notification — always update parent `aria-label` *(WAI-ARIA APG)*
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Anti-patterns Cross-links
|
|
152
|
+
|
|
153
|
+
| Anti-pattern | Entry |
|
|
154
|
+
|--------------|-------|
|
|
155
|
+
| Color as sole status differentiator | `reference/anti-patterns.md#ban-color-only` |
|
|
156
|
+
| Missing aria-label on parent with count | `reference/anti-patterns.md#ban-aria-label` |
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Benchmark Citations
|
|
161
|
+
|
|
162
|
+
| Claim | Sources |
|
|
163
|
+
|-------|---------|
|
|
164
|
+
| Badge is decorative; parent carries aria-label | Material 3, Polaris, Carbon, Radix |
|
|
165
|
+
| Count capped at "99+" | Material 3, Carbon, Radix |
|
|
166
|
+
| 16–20px height; min-width = height | Material 3, Polaris, Carbon |
|
|
167
|
+
| Zero badge hidden by default | Polaris, Radix |
|
|
168
|
+
| aria-hidden="true" on badge element | WAI-ARIA APG |
|
|
169
|
+
| Parent aria-label updated on count change | WCAG 4.1.3 |
|
|
170
|
+
|
|
171
|
+
Full system URLs: `connections/design-corpora.md`
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Grep Signatures
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Badge count not surfaced in parent aria-label
|
|
179
|
+
grep -rn 'badge\|Badge' src/ | grep -v 'aria-label' | grep -v 'aria-hidden'
|
|
180
|
+
|
|
181
|
+
# Badge element missing aria-hidden
|
|
182
|
+
grep -rn 'class.*badge\|className.*badge' src/ | grep -v 'aria-hidden="true"'
|
|
183
|
+
|
|
184
|
+
# Parent with badge but no aria-label
|
|
185
|
+
grep -rn 'badge\|Badge' src/ -l | xargs grep -l 'button\|icon\|avatar' | xargs grep -L 'aria-label'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Failing Example
|
|
191
|
+
|
|
192
|
+
```html
|
|
193
|
+
<!-- BAD: badge count visible but parent has no aria-label for screen readers -->
|
|
194
|
+
<button class="icon-btn">
|
|
195
|
+
<svg aria-hidden="true"><!-- bell icon --></svg>
|
|
196
|
+
<span class="badge">3</span>
|
|
197
|
+
</button>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Why it fails**: Screen readers announce "button" with no mention of the count. The `<span class="badge">3</span>` may be announced as isolated "3" out of context, or the badge text is read before the button label, producing confusing output. The button has no accessible name describing its purpose or the notification count.
|
|
201
|
+
**Grep detection**: `grep -rn 'class.*badge' src/ | xargs grep -B2 -A2 'button\|icon' | grep -v 'aria-label'`
|
|
202
|
+
**Fix**: `<button class="icon-btn" aria-label="Notifications, 3 unread"><svg aria-hidden="true">…</svg><span class="badge" aria-hidden="true">3</span></button>`
|