@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.
Files changed (58) hide show
  1. package/.claude-plugin/marketplace.json +12 -4
  2. package/.claude-plugin/plugin.json +22 -4
  3. package/CHANGELOG.md +111 -0
  4. package/README.md +27 -2
  5. package/agents/design-auditor.md +65 -1
  6. package/agents/design-context-builder.md +6 -1
  7. package/agents/design-doc-writer.md +21 -0
  8. package/agents/design-executor.md +22 -4
  9. package/agents/design-pattern-mapper.md +62 -0
  10. package/agents/design-phase-researcher.md +1 -1
  11. package/agents/motion-mapper.md +74 -9
  12. package/agents/token-mapper.md +8 -0
  13. package/package.json +16 -2
  14. package/reference/components/README.md +27 -23
  15. package/reference/components/alert.md +198 -0
  16. package/reference/components/badge.md +202 -0
  17. package/reference/components/breadcrumbs.md +198 -0
  18. package/reference/components/chip.md +209 -0
  19. package/reference/components/command-palette.md +228 -0
  20. package/reference/components/date-picker.md +227 -0
  21. package/reference/components/file-upload.md +219 -0
  22. package/reference/components/list.md +217 -0
  23. package/reference/components/menu.md +212 -0
  24. package/reference/components/navbar.md +211 -0
  25. package/reference/components/pagination.md +205 -0
  26. package/reference/components/progress.md +210 -0
  27. package/reference/components/rich-text-editor.md +226 -0
  28. package/reference/components/sidebar.md +211 -0
  29. package/reference/components/skeleton.md +197 -0
  30. package/reference/components/slider.md +208 -0
  31. package/reference/components/stepper.md +220 -0
  32. package/reference/components/table.md +229 -0
  33. package/reference/components/toast.md +200 -0
  34. package/reference/components/tree.md +225 -0
  35. package/reference/css-grid-layout.md +835 -0
  36. package/reference/data-visualization.md +333 -0
  37. package/reference/external/NOTICE.hyperframes +28 -0
  38. package/reference/form-patterns.md +245 -0
  39. package/reference/image-optimization.md +582 -0
  40. package/reference/information-architecture.md +255 -0
  41. package/reference/motion-advanced.md +754 -0
  42. package/reference/motion-easings.md +381 -0
  43. package/reference/motion-interpolate.md +282 -0
  44. package/reference/motion-spring.md +234 -0
  45. package/reference/motion-transition-taxonomy.md +155 -0
  46. package/reference/motion.md +20 -0
  47. package/reference/onboarding-progressive-disclosure.md +250 -0
  48. package/reference/output-contracts/motion-map.schema.json +135 -0
  49. package/reference/platforms.md +346 -0
  50. package/reference/registry.json +445 -220
  51. package/reference/registry.schema.json +4 -0
  52. package/reference/rtl-cjk-cultural.md +353 -0
  53. package/reference/user-research.md +360 -0
  54. package/reference/variable-fonts-loading.md +532 -0
  55. package/scripts/lib/easings.cjs +280 -0
  56. package/scripts/lib/parse-contract.cjs +220 -0
  57. package/scripts/lib/spring.cjs +160 -0
  58. package/scripts/tests/test-motion-provenance.sh +64 -0
@@ -0,0 +1,197 @@
1
+ # Skeleton — Benchmark Spec
2
+
3
+ **Harvested from**: Polaris, Carbon, Atlassian, Mantine
4
+ **Wave**: 3 · **Category**: Feedback
5
+
6
+ ---
7
+
8
+ ## Purpose
9
+
10
+ A skeleton screen is a loading placeholder that mirrors the shape of the content it will replace — text lines, images, cards, avatars. It reduces perceived wait time by showing the structural layout before real data arrives, preventing the jarring reflow that occurs when content suddenly appears. Use skeleton when the content shape is known; use an indeterminate spinner or progress bar when shape is unknown. *(Polaris, Carbon, Atlassian, Mantine agree: skeleton = shape-matched placeholder, not generic spinner)*
11
+
12
+ ---
13
+
14
+ ## Anatomy
15
+
16
+ ```
17
+ ┌──────────────────────────────────┐
18
+ │ ████ (avatar-circle, 40px) │ ← aria-hidden="true"
19
+ │ ██████████████████ (text-line) │
20
+ │ █████████████ (text-line 75%) │
21
+ │ ████████████████████ (text-line)│
22
+ └──────────────────────────────────┘
23
+ ↑ container: aria-busy="true" aria-label="Loading…"
24
+ ```
25
+
26
+ | Part | Required | Notes |
27
+ |------|----------|-------|
28
+ | Container | Yes | `aria-busy="true"` + `aria-label="Loading…"` or `aria-labelledby` |
29
+ | Skeleton elements | Yes | `aria-hidden="true"` on each shape element |
30
+ | Text-line shape | No | Width 60–90% (varied) to mimic text flow |
31
+ | Image/card block | No | Fixed aspect-ratio; fills the same space as the loaded image |
32
+ | Avatar circle | No | Circular shape; diameter matches the final avatar size |
33
+
34
+ ---
35
+
36
+ ## Variants
37
+
38
+ | Variant | Description | Systems |
39
+ |---------|-------------|---------|
40
+ | Text lines | One or more lines at varying widths (60–90%) | All |
41
+ | Image block | Rectangular/aspect-ratio shape for images | Polaris, Carbon, Mantine |
42
+ | Avatar | Circular placeholder at avatar diameter | Polaris, Atlassian, Mantine |
43
+ | Card | Full card-sized block with text-line children | All |
44
+ | Table row | Row-shaped block with column-width children | Carbon, Mantine |
45
+
46
+ **Norm** (≥4/18 systems agree): shimmer animation (left-to-right gradient sweep); vary text-line widths 60–90%; `aria-hidden` on skeleton elements; `aria-busy` on container.
47
+ **Diverge**: Polaris renders skeleton as named sub-components (`SkeletonBodyText`, `SkeletonDisplayText`); Carbon uses CSS class modifiers; Mantine uses a generic `Skeleton` with width/height props; Atlassian uses a shape prop.
48
+
49
+ ---
50
+
51
+ ## States
52
+
53
+ | State | Trigger | Visual | ARIA |
54
+ |-------|---------|--------|------|
55
+ | loading | data fetch in progress | Shimmer animation looping | `aria-busy="true"` on container |
56
+ | loaded | data resolves | Skeleton removed, real content appears | `aria-busy="false"` or remove attr |
57
+
58
+ ---
59
+
60
+ ## Sizing & Spacing
61
+
62
+ | Shape | Default sizing | Notes |
63
+ |-------|----------------|-------|
64
+ | Text-line | 16px height | Matches body line-height slot |
65
+ | Text-line (heading) | 24–28px height | Matches h2/h3 slot |
66
+ | Avatar | Match target avatar diameter | 32px, 40px, 48px common |
67
+ | Image block | Match target image aspect ratio | 16:9, 1:1, 4:3 common |
68
+ | Gap between text lines | 8px | Matches body line-height rhythm |
69
+
70
+ **Norm**: Match exact pixel dimensions of the content being replaced — layout shift score is zero when skeleton matches final content size *(Polaris, Mantine)*.
71
+
72
+ ---
73
+
74
+ ## Typography
75
+
76
+ Skeleton shapes are purely visual — no typography content. However:
77
+ - Text-line height should match the `line-height` of the real text it replaces
78
+ - Heading skeleton height should match the heading's `font-size` + leading
79
+ - Do NOT use placeholder text ("Loading…") inside skeleton shapes — use `aria-label` on the container instead
80
+
81
+ Cross-link: `reference/typography.md` — line-height scale for matching skeleton dimensions
82
+
83
+ ---
84
+
85
+ ## Keyboard & Accessibility
86
+
87
+ > **WAI-ARIA role**: no role on skeleton shapes; container uses `aria-busy`
88
+ > **Required attributes**: `aria-hidden="true"` on each skeleton element; `aria-busy="true"` + `aria-label="Loading…"` on container
89
+
90
+ ### Keyboard Contract
91
+
92
+ *Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/ — W3C — 2024*
93
+
94
+ | Key | Action |
95
+ |-----|--------|
96
+ | (none) | Skeleton shapes are not interactive; no keyboard interaction |
97
+
98
+ Skeleton elements must not receive focus. The container is not focusable unless it wraps focusable content that appears after loading.
99
+
100
+ ### Accessibility Rules
101
+
102
+ - Every skeleton shape element MUST have `aria-hidden="true"` — blank shapes announced by screen readers ("image", "text") confuse users *(Polaris, Carbon)*
103
+ - The container MUST have `aria-busy="true"` while loading, set to `false` (or removed) when content appears *(WAI-ARIA APG)*
104
+ - The container MUST have `aria-label="Loading…"` or equivalent — this is what screen readers announce while `aria-busy="true"` *(WAI-ARIA APG)*
105
+ - Do NOT use skeleton as the only loading indicator for screen reader users — announce loading state via live region if the transition is programmatic *(Carbon)*
106
+ - Shimmer animation MUST be suppressed under `prefers-reduced-motion` — use static fill instead *(WCAG 2.3.3)*
107
+
108
+ Cross-link: `reference/accessibility.md` — `aria-busy`, `prefers-reduced-motion`
109
+
110
+ ---
111
+
112
+ ## Motion
113
+
114
+ | Transition | Duration | Easing | Notes |
115
+ |------------|----------|--------|-------|
116
+ | Shimmer sweep | 1.5s | ease-in-out | 130° gradient: transparent → surface-highlight → transparent |
117
+ | Loop delay | 0.5s | — | Pause between sweeps to avoid strobing |
118
+ | Skeleton → content | 200ms | ease-out | Fade-in real content over skeleton |
119
+
120
+ Shimmer gradient direction: 130 degrees (roughly top-left to bottom-right) — matches natural reading direction.
121
+ Background: `surface-variant` token (slightly darker than background surface, lighter than border).
122
+
123
+ **BAN**: High-contrast shimmer (e.g. white → gray on dark) — too visually noisy. Do NOT use spinner animation inside a skeleton shape. Under `prefers-reduced-motion`, remove the sweep entirely and use static fill.
124
+
125
+ Cross-link: `reference/motion.md` — `prefers-reduced-motion`: static fill, no gradient sweep
126
+
127
+ ---
128
+
129
+ ## Do / Don't
130
+
131
+ ### Do
132
+ - Match skeleton dimensions exactly to target content to avoid layout shift *(Polaris, Mantine)*
133
+ - Vary text-line widths 60–90% to simulate natural text flow *(Carbon, Atlassian)*
134
+ - Set `aria-hidden="true"` on every skeleton shape element *(WAI-ARIA APG, Polaris)*
135
+ - Set `aria-busy="true"` + `aria-label="Loading…"` on the container *(WAI-ARIA APG)*
136
+
137
+ ### Don't
138
+ - Don't use a spinner when content shape is known — skeleton is always preferred for layout-bearing slots *(Carbon, Polaris)*
139
+ - Don't use strong contrast for shimmer — use `surface-variant` (low contrast) *(Atlassian, Mantine)*
140
+ - Don't animate shimmer under `prefers-reduced-motion` — use static fill *(WCAG 2.3.3)*
141
+ - Don't add visible "Loading…" text inside skeleton shapes — put it on the container via `aria-label` *(Carbon)*
142
+
143
+ ---
144
+
145
+ ## Anti-patterns Cross-links
146
+
147
+ | Anti-pattern | Entry |
148
+ |--------------|-------|
149
+ | Spinner used when content shape is known | `reference/anti-patterns.md#ban-spinner-overuse` |
150
+ | Missing aria-hidden on visual-only elements | `reference/anti-patterns.md#ban-aria-hidden` |
151
+
152
+ ---
153
+
154
+ ## Benchmark Citations
155
+
156
+ | Claim | Sources |
157
+ |-------|---------|
158
+ | aria-hidden="true" on skeleton shapes | WAI-ARIA APG, Polaris, Carbon |
159
+ | aria-busy="true" on container | WAI-ARIA APG |
160
+ | Text-line widths 60–90% varied | Carbon, Atlassian, Mantine |
161
+ | Shimmer 130° gradient sweep 1.5s | Polaris, Mantine |
162
+ | Suppress shimmer under prefers-reduced-motion | WCAG 2.3.3 |
163
+ | Match exact target dimensions to prevent layout shift | Polaris, Mantine |
164
+
165
+ Full system URLs: `connections/design-corpora.md`
166
+
167
+ ---
168
+
169
+ ## Grep Signatures
170
+
171
+ ```bash
172
+ # Skeleton shapes missing aria-hidden
173
+ grep -rn 'skeleton\|Skeleton' src/ | grep -v 'aria-hidden="true"' | grep -v 'aria-busy\|container'
174
+
175
+ # Skeleton container missing aria-busy
176
+ grep -rn 'skeleton\|Skeleton' src/ | grep 'container\|wrapper\|section' | grep -v 'aria-busy'
177
+
178
+ # Shimmer animation without prefers-reduced-motion guard
179
+ grep -rn 'shimmer\|skeleton.*animation\|@keyframes.*skeleton' src/ | grep -v 'prefers-reduced-motion'
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Failing Example
185
+
186
+ ```html
187
+ <!-- BAD: skeleton shapes with no aria-hidden, container with no aria-busy -->
188
+ <div class="card-skeleton">
189
+ <div class="skeleton-avatar"></div>
190
+ <div class="skeleton-line skeleton-line--80"></div>
191
+ <div class="skeleton-line skeleton-line--60"></div>
192
+ </div>
193
+ ```
194
+
195
+ **Why it fails**: Screen readers traverse the skeleton shapes and announce them as empty elements ("image", unlabeled regions). There is no `aria-busy="true"` to signal a loading state. No `aria-label` tells the user what is loading. When content loads, no `aria-busy="false"` transition signals completion.
196
+ **Grep detection**: `grep -rn 'skeleton' src/ | grep -v 'aria-hidden\|aria-busy'`
197
+ **Fix**: Add `aria-busy="true" aria-label="Loading card content"` to the container; add `aria-hidden="true"` to every skeleton child shape.
@@ -0,0 +1,208 @@
1
+ # Slider — Benchmark Spec
2
+
3
+ **Harvested from**: WAI-ARIA APG Slider pattern, Material 3, Radix Slider, Carbon Design System
4
+ **Wave**: 5 · **Category**: Advanced
5
+ **Spec file**: `reference/components/slider.md`
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ A Slider lets users select a numeric value (or range of values) by dragging a thumb along a track. It is appropriate when the range of values is meaningful as a continuum — volume, price, opacity, temperature — and the exact numeric value matters less than relative position. For precise numeric entry, pair the slider with a text input. For a range, use two thumbs. *(Material 3, Carbon, Radix agree: slider = continuous or discrete value selection with visual track.)*
12
+
13
+ ---
14
+
15
+ ## Anatomy
16
+
17
+ ```
18
+ Single:
19
+ [ ●────────────────────── ] ← track
20
+ thumb
21
+
22
+ Range:
23
+ [ ──────●────────●──────── ]
24
+ min max
25
+ thumb thumb
26
+ ```
27
+
28
+ | Part | Required | Notes |
29
+ |------|----------|-------|
30
+ | Track | Yes | Full-width bar; inactive segments use muted color |
31
+ | Active range fill | Yes | Filled portion between min-edge and thumb (single) or between thumbs (range) |
32
+ | Thumb | Yes | Draggable handle; visual ≥12px; touch target ≥44px via ::before padding |
33
+ | Value label (tooltip) | No | Shows current value above/beside thumb on interaction |
34
+ | Tick marks | No | Shown for discrete steps when ≤10 steps |
35
+ | Min/Max labels | No | Static text at track ends |
36
+ | Numeric input | No | Paired `<input type="number">` for precise entry |
37
+
38
+ ---
39
+
40
+ ## Variants
41
+
42
+ | Variant | Description | Systems |
43
+ |---------|-------------|---------|
44
+ | Single | One thumb; selects a scalar value | WAI-ARIA APG, Material 3, Carbon, Radix |
45
+ | Range | Two thumbs; selects min and max of a range | Material 3 (RangeSlider), Carbon, Radix |
46
+ | Discrete | Step-snapping with tick marks (≤10 steps) | Material 3, Carbon |
47
+ | Continuous | No snapping; free movement | Material 3, Carbon, Radix |
48
+ | Vertical | `aria-orientation="vertical"`; track runs top-to-bottom | WAI-ARIA APG, Carbon |
49
+
50
+ **Norm** (≥3/4 systems agree): horizontal orientation is default; thumb is a circle on a horizontal track; active range fills in brand color.
51
+ **Diverge**: Carbon shows tick labels below the track; Material 3 shows a floating value tooltip on drag; Radix delegates tooltip entirely to the consumer.
52
+
53
+ ---
54
+
55
+ ## States
56
+
57
+ | State | Trigger | Visual | ARIA |
58
+ |-------|---------|--------|------|
59
+ | default | — | Track + thumb at resting position | `aria-valuenow` reflects current value |
60
+ | hover | Pointer over thumb | Thumb expands or shows halo | — |
61
+ | focus | Keyboard focus on thumb | Focus-visible ring on thumb | — |
62
+ | dragging / active | Mousedown / touch on thumb | Value tooltip visible; thumb slightly enlarged | — |
63
+ | disabled | `disabled` / `aria-disabled` | 38% opacity; cursor: not-allowed | `aria-disabled="true"` |
64
+
65
+ ---
66
+
67
+ ## Sizing & Spacing
68
+
69
+ | Size | Track Height | Thumb Visual | Thumb Hit Area | Notes |
70
+ |------|-------------|--------------|----------------|-------|
71
+ | sm | 2px | 12px | 44px (via ::before) | Compact; pair with numeric input |
72
+ | md (default) | 4px | 20px | 44px (via ::before) | Standard; tick labels at 14px |
73
+ | lg | 6px | 24px | 44px | High-emphasis; price/volume controls |
74
+
75
+ **Norm**: 4px track height (Material 3, Carbon). Thumb visual diameter 20px with ::before/::after expanding the touch target to ≥44px without inflating layout.
76
+
77
+ Cross-link: `reference/surfaces.md` — 44×44px touch-target minimum; use padding trick, not enlarged visual.
78
+
79
+ ---
80
+
81
+ ## Typography
82
+
83
+ - Value tooltip: caption-sm, weight 500, centered above thumb
84
+ - Tick labels: caption-xs, secondary color, centered below tick mark
85
+ - Min/Max endpoint labels: caption-sm, secondary color, flush with track ends
86
+
87
+ Cross-link: `reference/typography.md` — tabular-nums on value tooltip so digits don't shift width during drag.
88
+
89
+ ---
90
+
91
+ ## Keyboard & Accessibility
92
+
93
+ > **WAI-ARIA role**: `slider`
94
+ > **Required attributes**: `aria-valuenow`, `aria-valuemin`, `aria-valuemax`; `aria-valuetext` for human-readable value (e.g., "$45" or "45%"); `aria-label` or `aria-labelledby`; `aria-orientation="vertical"` when vertical
95
+
96
+ ### Keyboard Contract
97
+
98
+ *Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/patterns/slider/ — W3C — 2024*
99
+
100
+ | Key | Action |
101
+ |-----|--------|
102
+ | Right Arrow / Up Arrow | Increase value by one step |
103
+ | Left Arrow / Down Arrow | Decrease value by one step |
104
+ | Page Up | Increase value by a larger step (typically 10% of range) |
105
+ | Page Down | Decrease value by a larger step (typically 10% of range) |
106
+ | Home | Set value to minimum |
107
+ | End | Set value to maximum |
108
+
109
+ *For vertical slider (`aria-orientation="vertical"`), Up Arrow increases and Down Arrow decreases.*
110
+
111
+ ### Accessibility Rules
112
+
113
+ - Every slider thumb MUST have `aria-valuenow`; update it continuously during drag
114
+ - `aria-valuetext` MUST be provided when raw number is not human-readable (e.g., "Low", "Medium", "High" for a quality setting, or "$45" for a price)
115
+ - Range slider: label each thumb distinctly (e.g., `aria-label="Minimum price"` and `aria-label="Maximum price"`) — identical labels confuse screen readers
116
+ - Thumb touch target MUST be ≥44×44px; use `::before`/`::after` pseudo-element padding if visual thumb is smaller
117
+ - Do not use `<input type="range">` hidden behind a custom div without ARIA — the native element is preferable when no custom styling is required
118
+ - Disabled sliders: use `aria-disabled="true"`; keep thumb in tab order so AT can announce the current value
119
+
120
+ Cross-link: `reference/accessibility.md` — slider role, aria-valuetext guidance.
121
+
122
+ ---
123
+
124
+ ## Motion
125
+
126
+ | Transition | Duration | Easing | Notes |
127
+ |------------|----------|--------|-------|
128
+ | Thumb drag | 0ms | — | No easing on drag — follows pointer exactly |
129
+ | Keyboard step | 80ms | ease-out | Smooth snap to new position |
130
+ | Value tooltip appear | 100ms | ease-out | Fade in on focus/drag |
131
+ | Value tooltip dismiss | 150ms | ease-in | Fade out on blur |
132
+ | Tick mark appear | 120ms | ease-out | When switching to discrete mode |
133
+
134
+ **BAN**: Do not add momentum or inertia easing to thumb drag — feels broken and breaks accessibility (thumb does not match pointer).
135
+
136
+ Cross-link: `reference/motion.md` — reduced-motion: remove keyboard-step animation; thumb jumps instantly.
137
+
138
+ ---
139
+
140
+ ## Do / Don't
141
+
142
+ ### Do
143
+ - Provide `aria-valuetext` with a human-readable label when the raw number needs context *(WAI-ARIA APG)*
144
+ - Give each range thumb a unique, descriptive `aria-label` *(WAI-ARIA APG, Radix Slider docs)*
145
+ - Expand thumb touch target to ≥44px with pseudo-element padding *(Material 3, Carbon)*
146
+ - Show tick marks only for discrete sliders with ≤10 steps *(Material 3, Carbon)*
147
+
148
+ ### Don't
149
+ - Don't build a slider from `<div>` with mouse events only — no keyboard, no ARIA *(diverges from all 4 systems)*
150
+ - Don't omit `aria-valuenow` updates during drag — AT users hear a stale value *(WAI-ARIA APG)*
151
+ - Don't let the visual thumb area be smaller than 12px with no hit-area expansion — fails WCAG 2.5.8 *(Material 3)*
152
+ - Don't use identical `aria-label` for both range thumbs *(WAI-ARIA APG)*
153
+
154
+ ---
155
+
156
+ ## Anti-patterns Cross-links
157
+
158
+ | Anti-pattern | Entry |
159
+ |--------------|-------|
160
+ | BAN-09 | Custom interactive widget with mouse events only, no keyboard — `reference/anti-patterns.md#ban-09` |
161
+ | BAN-11 | Touch target below 44px without padding expansion — `reference/anti-patterns.md#ban-11` |
162
+
163
+ ---
164
+
165
+ ## Benchmark Citations
166
+
167
+ | Claim | Sources |
168
+ |-------|---------|
169
+ | role="slider" + aria-valuenow/min/max required | WAI-ARIA APG Slider pattern |
170
+ | Arrow key step, Page key 10% step, Home/End to extremes | WAI-ARIA APG keyboard contract |
171
+ | 4px track height default | Material 3, Carbon |
172
+ | Thumb touch target ≥44px via pseudo-element | Material 3, Carbon accessibility guidelines |
173
+ | aria-valuetext for non-numeric human-readable values | WAI-ARIA APG, Radix Slider docs |
174
+
175
+ Full system URLs: `connections/design-corpora.md`
176
+
177
+ ---
178
+
179
+ ## Grep Signatures
180
+
181
+ ```bash
182
+ # Slider thumb missing aria-valuenow (ARIA contract violation)
183
+ grep -rn 'role="slider"' src/ | grep -v 'aria-valuenow'
184
+
185
+ # Custom slider div with mouse events only — no keyboard handler
186
+ grep -rn 'class.*slider\|\.slider' src/ | grep 'onMouseDown\|mousedown' | grep -v 'onKeyDown\|keydown\|role="slider"'
187
+
188
+ # Thumb element potentially below 44px with no hit-area expansion
189
+ grep -rn '\.thumb\|slider-thumb' src/ | grep 'width:\s*[0-9]\{1,2\}px\|height:\s*[0-9]\{1,2\}px' | grep -v '::before\|::after\|padding'
190
+
191
+ # Range slider thumbs with identical aria-label
192
+ grep -rn 'role="slider"' src/ -A2 | grep 'aria-label' | sort | uniq -d
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Failing Example
198
+
199
+ ```html
200
+ <!-- BAD: custom slider using <div> with mouse events only — no keyboard, no ARIA -->
201
+ <div class="slider-track" onmousedown="startDrag(event)">
202
+ <div class="slider-thumb" style="left: 45%"></div>
203
+ </div>
204
+ ```
205
+
206
+ **Why it fails**: Not reachable by keyboard; no `role="slider"`; no `aria-valuenow`, `aria-valuemin`, or `aria-valuemax`; AT cannot read or change the value; touch users cannot interact via assistive touch.
207
+ **Grep detection**: `grep -rn '<div.*slider\|class="slider' src/ | grep -v 'role="slider"'`
208
+ **Fix**: Use native `<input type="range">` or add `role="slider"` + `aria-valuenow` + `aria-valuemin` + `aria-valuemax` + `aria-valuetext` + full keyboard event handlers (Arrow, Page, Home, End) to the thumb element.
@@ -0,0 +1,220 @@
1
+ # Stepper / Wizard — Benchmark Spec
2
+
3
+ **Harvested from**: Carbon (ProgressIndicator + StepNavigation), Material 3 Stepper, Atlassian Design System, Mantine Stepper
4
+ **Wave**: 5 · **Category**: Advanced
5
+ **Spec file**: `reference/components/stepper.md`
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ A Stepper (or Wizard) guides users through a sequential multi-step flow — onboarding, checkout, multi-page forms, settings setup. It communicates how many steps exist, which step is current, which are complete, and which are upcoming. Unlike Tabs (free navigation), a linear Stepper enforces order: the user must complete the current step before advancing. *(Carbon, Material 3, Atlassian, Mantine agree: step indicator list + content area + explicit Next/Back buttons is the canonical wizard pattern.)*
12
+
13
+ ---
14
+
15
+ ## Anatomy
16
+
17
+ ```
18
+ Step indicator (role="list"):
19
+ ● Step 1: Account ✓ Step 2: Profile ○ Step 3: Confirm
20
+ aria-current="step" completed upcoming
21
+
22
+ Content area:
23
+ [ Current step form/content ]
24
+
25
+ Actions:
26
+ [ Back ] [ Next ] / [ Submit ]
27
+ ```
28
+
29
+ | Part | Required | Notes |
30
+ |------|----------|-------|
31
+ | Step indicator list | Yes | `role="list"`; each step is `role="listitem"` |
32
+ | Step labels | Yes | Visible text per step; described state (e.g., "completed") |
33
+ | Current step marker | Yes | `aria-current="step"` on active step |
34
+ | Content area | Yes | Shows current step content (single panel or accordion) |
35
+ | Next button | Yes | Explicit label "Next" or "Continue"; validates current step first |
36
+ | Back button | Yes (if non-first step) | Returns to previous step; "Back" label |
37
+ | Submit button | Yes (final step) | "Submit" or context-specific label; replaces Next on last step |
38
+ | Step connector line | No | Visual line between step dots; decorative, `aria-hidden="true"` |
39
+
40
+ ---
41
+
42
+ ## Variants
43
+
44
+ | Variant | Description | Systems |
45
+ |---------|-------------|---------|
46
+ | Linear / locked | Must complete steps in order; no jumping ahead | Carbon, Material 3, Atlassian, Mantine |
47
+ | Non-linear | Can jump to any previously completed step | Carbon (StepNavigation), Mantine (allowNextStepsSelect) |
48
+ | Horizontal | Step indicators in a row across the top | All systems (default) |
49
+ | Vertical | Step indicators stacked on the left | Carbon, Material 3 |
50
+ | Accordion stepper | All steps visible; current step expanded | Material 3 (docked), Mantine (vertical) |
51
+ | Simple (no icons) | Text + number only, no check icons | Atlassian (compact) |
52
+
53
+ **Norm** (≥3/4 systems agree): horizontal orientation is default; completed steps show a checkmark; current step is visually distinct; upcoming steps are muted.
54
+ **Diverge**: Material 3 uses filled circles with numbers; Carbon uses custom step icons; Mantine supports icons per step; Atlassian uses numbered circles.
55
+
56
+ ---
57
+
58
+ ## States
59
+
60
+ | State | Trigger | Visual | ARIA |
61
+ |-------|---------|--------|------|
62
+ | upcoming | Step not yet reached | Muted circle/number, secondary text color | — |
63
+ | current | Active step | Brand-color filled circle; bold label | `aria-current="step"` |
64
+ | completed | Step passed + valid | Check icon; full-opacity; clickable if non-linear | `aria-label="Step N: [name] - completed"` |
65
+ | error | Step has validation errors | Error color circle; error icon | `aria-label="Step N: [name] - has errors"` |
66
+ | disabled | Future step in linear flow | Muted; not clickable | Implicit (no click handler; cursor: default) |
67
+
68
+ ---
69
+
70
+ ## Sizing & Spacing
71
+
72
+ | Size | Step Circle | Connector Height | Label Font | Gap Between Steps |
73
+ |------|-------------|-----------------|------------|-------------------|
74
+ | sm | 20px | 1px | 12px | 32px |
75
+ | md (default) | 32px | 2px | 14px | 48px |
76
+ | lg | 40px | 2px | 16px | 64px |
77
+
78
+ **Norm**: Step circles 32px default *(Carbon, Mantine)*. Horizontal spacing between steps should scale with the available width so the indicator spans the container. Connector line is centered between circles.
79
+
80
+ Cross-link: `reference/surfaces.md` — minimum 44×44px touch target for clickable step indicators.
81
+
82
+ ---
83
+
84
+ ## Typography
85
+
86
+ - Step label: body-sm (completed/upcoming) → body-sm weight 600 (current)
87
+ - Step number/icon inside circle: caption-sm, center-aligned
88
+ - Step description (optional sub-label): caption-sm, secondary color
89
+ - Action buttons: body-md, weight 500 — same as standard button spec
90
+
91
+ Cross-link: `reference/typography.md` — label weight change for current state.
92
+
93
+ ---
94
+
95
+ ## Keyboard & Accessibility
96
+
97
+ > **WAI-ARIA role**: `list` (step indicator container), `listitem` (each step), `button` (clickable completed steps in non-linear mode)
98
+ > **Required attributes**: `aria-current="step"` on active step; descriptive `aria-label` on completed steps (include state: "Step 2: Profile - completed"); `aria-label` on connector lines if not `aria-hidden`
99
+
100
+ ### Keyboard Contract
101
+
102
+ *Derived from WAI-ARIA APG list and button patterns — https://www.w3.org/WAI/ARIA/apg/ — W3C — 2024*
103
+
104
+ | Key | Action |
105
+ |-----|--------|
106
+ | Tab | Move focus through interactive elements: clickable completed steps (non-linear), Back button, Next/Submit button |
107
+ | Enter / Space | Activate focused Back, Next, Submit button; activate clickable completed step (non-linear) |
108
+ | (No arrow-key navigation) | Steps are NOT tabs — do not implement roving tabindex / arrow-key navigation between steps |
109
+
110
+ Steps are NOT `role="tab"` and do not use the tab keyboard pattern. Upcoming steps are not focusable in linear mode. Only completed steps are interactive (and focusable) in non-linear mode.
111
+
112
+ ### Accessibility Rules
113
+
114
+ - Step indicators MUST use `role="list"` + `role="listitem"` — not `role="tablist"` + `role="tab"` (tabs allow free navigation; wizard steps do not)
115
+ - Current step MUST have `aria-current="step"` — this is the correct token (not `aria-selected` or `aria-checked`)
116
+ - Completed steps in non-linear mode MUST be `<button>` elements (or have `role="button"` + `tabindex="0"`) with `aria-label` including the step name and "completed" state
117
+ - Step connector lines MUST be `aria-hidden="true"` — they are purely decorative
118
+ - Validate current step before allowing Next; display inline error messages with `aria-describedby` associations
119
+ - "Next", "Back", and "Submit" MUST be explicit text labels — do not use icon-only navigation buttons
120
+ - Announce step transitions via `aria-live="polite"` on the content region header (e.g., "Step 2 of 4: Profile")
121
+
122
+ Cross-link: `reference/accessibility.md` — aria-current values, list semantics.
123
+
124
+ ---
125
+
126
+ ## Motion
127
+
128
+ | Transition | Duration | Easing | Notes |
129
+ |------------|----------|--------|-------|
130
+ | Step complete animation | 200ms | ease-out | Circle fill → checkmark draw |
131
+ | Content area transition | 250ms | ease-in-out | Fade or slide left/right between steps |
132
+ | Error state appear | 150ms | ease-out | Circle color change + icon fade in |
133
+ | Back navigation | 200ms | ease-in-out | Slide right (reverse direction) |
134
+
135
+ **BAN**: Do not use the same slide direction for both forward and backward step navigation — direction must be consistent with the mental model (forward = left, backward = right).
136
+
137
+ Cross-link: `reference/motion.md` — reduced-motion: skip slide; cross-fade content area only.
138
+
139
+ ---
140
+
141
+ ## Do / Don't
142
+
143
+ ### Do
144
+ - Use `role="list"` for the step indicator — steps are a list, not a tab set *(WAI-ARIA APG, Carbon)*
145
+ - Set `aria-current="step"` on the active step *(WAI-ARIA spec §aria-current)*
146
+ - Validate the current step before advancing and show inline errors *(Carbon, Atlassian)*
147
+ - Label Back/Next/Submit buttons explicitly — not icons or chevrons *(Material 3, Carbon, Mantine)*
148
+
149
+ ### Don't
150
+ - Don't use `role="tablist"` for steps — tabs allow free navigation; wizard steps are ordered and gated *(diverges from all 4 systems)*
151
+ - Don't allow jumping to future unvisited steps in linear mode — breaks the sequential contract *(Carbon, Atlassian)*
152
+ - Don't use `aria-selected` on steps — `aria-current="step"` is the correct token *(WAI-ARIA spec)*
153
+ - Don't omit the Back button — users must be able to correct previous steps *(Material 3, Atlassian)*
154
+
155
+ ---
156
+
157
+ ## Anti-patterns Cross-links
158
+
159
+ | Anti-pattern | Entry |
160
+ |--------------|-------|
161
+ | BAN-14 | Stepper using role="tablist" — semantically incorrect navigation model — `reference/anti-patterns.md#ban-14` |
162
+ | BAN-07 | Missing aria-current on active step — `reference/anti-patterns.md#ban-07` |
163
+
164
+ ---
165
+
166
+ ## Benchmark Citations
167
+
168
+ | Claim | Sources |
169
+ |-------|---------|
170
+ | Step indicator is role="list", not role="tablist" | WAI-ARIA APG, Carbon, Atlassian design docs |
171
+ | aria-current="step" on active step | WAI-ARIA spec §aria-current, Carbon a11y guide |
172
+ | Completed steps need aria-label with state | Carbon ProgressIndicator a11y docs, Atlassian |
173
+ | Validate before Next; show inline errors | Carbon, Atlassian wizard pattern guidelines |
174
+ | Explicit Next/Back/Submit text labels required | Material 3, Carbon, Mantine |
175
+
176
+ Full system URLs: `connections/design-corpora.md`
177
+
178
+ ---
179
+
180
+ ## Grep Signatures
181
+
182
+ ```bash
183
+ # Step indicator using role="tablist" (incorrect — steps are not tabs)
184
+ grep -rn 'stepper\|wizard\|step-indicator\|StepIndicator' src/ | grep 'role="tablist"'
185
+
186
+ # Missing aria-current on active step
187
+ grep -rn 'stepper\|wizard\|\.step\b\|step--active\|step--current' src/ | grep -v 'aria-current'
188
+
189
+ # Step connector lines not aria-hidden (extraneous AT noise)
190
+ grep -rn 'step.*connector\|connector.*step\|\.step-line\|StepConnector' src/ | grep -v 'aria-hidden'
191
+
192
+ # Icon-only Next/Back buttons (no text label)
193
+ grep -rn 'wizard.*next\|stepper.*next\|wizard.*back\|stepper.*back' src/ | grep -v 'Next\|Back\|Continue\|Submit\|aria-label'
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Failing Example
199
+
200
+ ```html
201
+ <!-- BAD: stepper using <ul role="tablist"> with <li role="tab"> — semantically wrong -->
202
+ <ul role="tablist" class="stepper">
203
+ <li role="tab" aria-selected="true" class="step step--active">
204
+ <span class="step-number">1</span>
205
+ <span class="step-label">Account</span>
206
+ </li>
207
+ <li role="tab" aria-selected="false" class="step step--upcoming">
208
+ <span class="step-number">2</span>
209
+ <span class="step-label">Profile</span>
210
+ </li>
211
+ <li role="tab" aria-selected="false" class="step step--upcoming">
212
+ <span class="step-number">3</span>
213
+ <span class="step-label">Confirm</span>
214
+ </li>
215
+ </ul>
216
+ ```
217
+
218
+ **Why it fails**: `role="tablist"` implies that all tabs are independently activatable and content switches freely — this is correct for Tabs but wrong for a wizard where steps are gated. AT users expect arrow-key navigation between tabs; in a wizard this would allow jumping to uncompleted future steps. `aria-selected` is the tab token; the correct token for a wizard is `aria-current="step"`.
219
+ **Grep detection**: `grep -rn 'role="tablist"' src/ | grep -i 'step\|wizard'`
220
+ **Fix**: Replace `<ul role="tablist">` with `<ol role="list">` (ordered list signals sequence), each `<li>` with `role="listitem"`, remove `aria-selected`, and add `aria-current="step"` to the current step. Make only completed steps keyboard-focusable (as `<button>`) in non-linear mode; upcoming steps are not interactive.