@hegemonart/get-design-done 1.16.0 → 1.18.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 (49) hide show
  1. package/.claude-plugin/marketplace.json +7 -5
  2. package/.claude-plugin/plugin.json +17 -5
  3. package/CHANGELOG.md +84 -0
  4. package/README.md +20 -2
  5. package/agents/design-auditor.md +60 -1
  6. package/agents/design-doc-writer.md +21 -0
  7. package/agents/design-executor.md +22 -4
  8. package/agents/design-pattern-mapper.md +61 -0
  9. package/agents/motion-mapper.md +74 -9
  10. package/agents/token-mapper.md +8 -0
  11. package/package.json +10 -2
  12. package/reference/components/README.md +27 -23
  13. package/reference/components/alert.md +198 -0
  14. package/reference/components/badge.md +202 -0
  15. package/reference/components/breadcrumbs.md +198 -0
  16. package/reference/components/chip.md +209 -0
  17. package/reference/components/command-palette.md +228 -0
  18. package/reference/components/date-picker.md +227 -0
  19. package/reference/components/file-upload.md +219 -0
  20. package/reference/components/list.md +217 -0
  21. package/reference/components/menu.md +212 -0
  22. package/reference/components/navbar.md +211 -0
  23. package/reference/components/pagination.md +205 -0
  24. package/reference/components/progress.md +210 -0
  25. package/reference/components/rich-text-editor.md +226 -0
  26. package/reference/components/sidebar.md +211 -0
  27. package/reference/components/skeleton.md +197 -0
  28. package/reference/components/slider.md +208 -0
  29. package/reference/components/stepper.md +220 -0
  30. package/reference/components/table.md +229 -0
  31. package/reference/components/toast.md +200 -0
  32. package/reference/components/tree.md +225 -0
  33. package/reference/css-grid-layout.md +835 -0
  34. package/reference/external/NOTICE.hyperframes +28 -0
  35. package/reference/image-optimization.md +582 -0
  36. package/reference/motion-advanced.md +754 -0
  37. package/reference/motion-easings.md +381 -0
  38. package/reference/motion-interpolate.md +282 -0
  39. package/reference/motion-spring.md +234 -0
  40. package/reference/motion-transition-taxonomy.md +155 -0
  41. package/reference/motion.md +20 -0
  42. package/reference/output-contracts/motion-map.schema.json +135 -0
  43. package/reference/registry.json +183 -0
  44. package/reference/registry.schema.json +4 -0
  45. package/reference/variable-fonts-loading.md +532 -0
  46. package/scripts/lib/easings.cjs +280 -0
  47. package/scripts/lib/parse-contract.cjs +220 -0
  48. package/scripts/lib/spring.cjs +160 -0
  49. package/scripts/tests/test-motion-provenance.sh +64 -0
@@ -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.
@@ -0,0 +1,229 @@
1
+ # Table (Data Table / Data Grid) — Benchmark Spec
2
+
3
+ **Harvested from**: Carbon DataTable, Polaris DataTable, Atlassian DynamicTable, Ant Design Table, UUPM (app-interface, MIT)
4
+ **Wave**: 4 · **Category**: Navigation & Data
5
+
6
+ ---
7
+
8
+ ## Purpose
9
+
10
+ A data table presents structured, comparable information in rows and columns. It supports sorting, filtering, row selection, and pagination. Use `role="table"` for static display; use `role="grid"` for interactive tables where keyboard navigation between cells is needed (e.g., spreadsheet-like editing). Tables are distinct from Lists (unstructured items) and Cards (single-entity display). *(Carbon DataTable, Polaris DataTable, Atlassian DynamicTable, Ant Table all define table as the canonical multi-column data display)*
11
+
12
+ ---
13
+
14
+ ## Anatomy
15
+
16
+ ```
17
+ ┌─────────────────────────────────────────────────────┐
18
+ │ [☐] Name ↑ Status Amount Actions │ <thead>
19
+ │─────────────────────────────────────────────────────│
20
+ │ [☐] Alice Chen Active $1,200.00 [···] │ <tbody>
21
+ │ [☑] Bob Tanaka Inactive $850.00 [···] │ aria-selected="true"
22
+ │ [☐] Carol Wu Active $2,400.00 [···] │
23
+ │─────────────────────────────────────────────────────│
24
+ │ Showing 1–3 of 247 [‹ Prev] 1 2 3 [Next ›] │ <tfoot>
25
+ └─────────────────────────────────────────────────────┘
26
+ ```
27
+
28
+ | Part | Required | Notes |
29
+ |------|----------|-------|
30
+ | `<table>` | Yes | Semantic table element; `role="grid"` if interactive |
31
+ | `<caption>` | Yes (or `aria-label`) | Describes the table's purpose; `<caption>` preferred |
32
+ | `<thead>` | Yes | Column header row(s) |
33
+ | `<th scope="col">` | Yes | `scope="col"` on every column header |
34
+ | `<tbody>` | Yes | Data rows |
35
+ | `<th scope="row">` | No | Row header for the first cell if rows have identity |
36
+ | `<tfoot>` | No | Summary row, pagination, totals |
37
+ | Sortable header | No | `aria-sort="ascending|descending|none"` on `<th>` |
38
+ | Select-all checkbox | No | `<th scope="col">` with select-all; `aria-label="Select all rows"` |
39
+ | Row checkbox | No | `<td>` with checkbox; selected row has `aria-selected="true"` on `<tr>` |
40
+ | Scroll wrapper | Conditional | `overflow-x: auto` + `tabindex="0"` on wrapper for keyboard scroll |
41
+
42
+ ---
43
+
44
+ ## Variants
45
+
46
+ | Variant | Description | Systems |
47
+ |---------|-------------|---------|
48
+ | Static / display | Read-only; `role="table"` | All systems |
49
+ | Sortable | Clickable column headers; `aria-sort` | Carbon, Polaris, Atlassian, Ant |
50
+ | Selectable | Row checkboxes; batch actions toolbar | Carbon, Polaris, Atlassian |
51
+ | Expandable rows | Toggle row detail panel | Carbon, Ant, Atlassian |
52
+ | Interactive / grid | Cell-level focus; `role="grid"` | Carbon, Ant |
53
+ | Sticky header | `position: sticky` on `<thead>` | Carbon, Ant, Polaris |
54
+ | Master-detail | Table + side detail pane | UUPM app-interface (MIT) |
55
+ | Dashboard data grid | Dense, compact variant for analytics dashboards | UUPM app-interface (MIT) |
56
+
57
+ **Norm** (≥4 systems agree): `<table>` with `<thead>`/`<tbody>`; `scope="col"` on all `<th>`; `aria-sort` on sortable columns.
58
+ **Diverge**: Carbon uses `role="grid"` by default for keyboard cell navigation; Polaris uses `role="table"` for read-only display.
59
+
60
+ ---
61
+
62
+ ## States
63
+
64
+ | State | Trigger | Visual | ARIA |
65
+ |-------|---------|--------|------|
66
+ | default | — | Alternating row colors or border separators | — |
67
+ | row-hover | pointer over row | Subtle row highlight (4% overlay) | — |
68
+ | row-selected | checkbox checked | Row highlight; checkbox filled | `aria-selected="true"` on `<tr>` |
69
+ | header-sort-asc | sort click | Arrow indicator up; column highlight | `aria-sort="ascending"` on `<th>` |
70
+ | header-sort-desc | sort click again | Arrow indicator down | `aria-sort="descending"` on `<th>` |
71
+ | header-sort-none | default or reset | No indicator | `aria-sort="none"` on sortable `<th>` |
72
+ | header-focus | keyboard focus on sortable `<th>` | 2px focus-visible ring | — |
73
+ | loading | data fetch | Skeleton rows or spinner overlay | `aria-busy="true"` on table or container |
74
+ | empty | no results | Empty state illustration + message | — |
75
+
76
+ ---
77
+
78
+ ## Sizing & Spacing
79
+
80
+ | Density | Row height | Cell padding H | Cell padding V | Font |
81
+ |---------|------------|----------------|----------------|------|
82
+ | compact | 32px | 12px | 4px | 13px |
83
+ | default | 48px | 16px | 12px | 14px |
84
+ | comfortable | 56px | 20px | 16px | 14px |
85
+
86
+ **Norm**: 48px default row height (Carbon, Atlassian, Ant). Column min-width 80px; text columns flexible.
87
+ Virtualise rows when `rowCount > 200`; use TanStack Virtual or react-virtual.
88
+
89
+ ---
90
+
91
+ ## Typography
92
+
93
+ - Column header: body-sm or label-sm (12–13px), weight 600, `color: --text-secondary`
94
+ - Cell text: body-sm (13–14px), weight 400
95
+ - Numeric cells: `font-variant-numeric: tabular-nums` — column values align on decimal point
96
+ - Truncation: `text-overflow: ellipsis` on cells with `max-width`; tooltip reveals full value on hover
97
+
98
+ Cross-link: `reference/typography.md` — tabular-nums, body-sm
99
+
100
+ ---
101
+
102
+ ## Keyboard & Accessibility
103
+
104
+ > **WAI-ARIA role**: `table` (static) or `grid` (interactive)
105
+ > **Required attributes**: `scope="col"` on all `<th>` headers; `aria-sort` on sortable columns; `aria-selected="true"` on selected `<tr>`; `<caption>` or `aria-label` on `<table>`
106
+
107
+ ### Keyboard Contract
108
+
109
+ *Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/patterns/grid/ — W3C — 2024*
110
+
111
+ | Key | Action (role="grid") |
112
+ |-----|---------------------|
113
+ | ArrowRight | Moves focus to next cell in row |
114
+ | ArrowLeft | Moves focus to previous cell in row |
115
+ | ArrowDown | Moves focus to same cell in next row |
116
+ | ArrowUp | Moves focus to same cell in previous row |
117
+ | Home | Moves focus to first cell in row |
118
+ | End | Moves focus to last cell in row |
119
+ | Ctrl+Home | Moves focus to first cell in grid |
120
+ | Ctrl+End | Moves focus to last cell in grid |
121
+ | Enter / Space | Activates cell widget (checkbox, link, button) |
122
+ | Tab | Moves focus out of grid to next component |
123
+
124
+ ### Accessibility Rules
125
+
126
+ - ALL `<th>` column headers MUST have `scope="col"` — missing scope breaks AT table navigation
127
+ - Sortable `<th>` elements MUST have `aria-sort` with value `ascending`, `descending`, or `none`
128
+ - Selected rows MUST use `aria-selected="true"` on `<tr>` — CSS class alone is invisible to AT
129
+ - `<table>` MUST have a `<caption>` or `aria-label` to announce the table's purpose
130
+ - The responsive scroll wrapper MUST have `tabindex="0"` so keyboard users can scroll horizontally
131
+ - `role="grid"` enables cell-level arrow-key navigation; use only when cells contain interactive controls
132
+
133
+ Cross-link: `reference/accessibility.md` — table semantics, grid pattern
134
+
135
+ ---
136
+
137
+ ## Motion
138
+
139
+ | Transition | Duration | Easing | Notes |
140
+ |------------|----------|--------|-------|
141
+ | Row hover highlight | 80ms | ease-out | Background color only |
142
+ | Row select | 100ms | ease-out | Checkbox + row color |
143
+ | Sort indicator change | 120ms | ease-out | Arrow direction transition |
144
+ | Expandable row open | 150ms | ease-out | Height expand |
145
+ | Skeleton shimmer | 1500ms | linear loop | Loading placeholder |
146
+
147
+ **BAN**: Do not animate `width` on table columns — causes full table relayout on every frame.
148
+
149
+ Cross-link: `reference/motion.md` — layout-affecting transitions, skeleton shimmer
150
+
151
+ ---
152
+
153
+ ## Do / Don't
154
+
155
+ ### Do
156
+ - Add `scope="col"` to every `<th>` — screen readers use this to announce column context *(WAI-ARIA, Carbon)*
157
+ - Use `aria-sort` on sortable headers — not just a visual arrow icon *(Carbon, Polaris, Atlassian)*
158
+ - Use `tabindex="0"` on horizontal scroll wrapper for keyboard accessibility *(WCAG 2.1.1)*
159
+ - Virtualise rows at > 200 items — prevents browser paint lag *(Carbon, Ant)*
160
+
161
+ ### Don't
162
+ - Don't build a "table" with `<div>` elements and no ARIA grid roles — invisible to AT *(WCAG 1.3.1)*
163
+ - Don't use `aria-sort` on non-sortable columns — misleads users into clicking non-interactive headers *(WAI-ARIA)*
164
+ - Don't place `overflow-x: auto` on `<table>` directly — wrap in a `<div>` with `tabindex="0"` *(WCAG 2.1.1)*
165
+ - Don't use `display: contents` on `<thead>/<tbody>/<tr>` — breaks AT table parsing *(WCAG 1.3.1)*
166
+
167
+ ---
168
+
169
+ ## Anti-patterns Cross-links
170
+
171
+ | Anti-pattern | Entry |
172
+ |--------------|-------|
173
+ | BAN-04 | `transition: all` on table cells — `reference/anti-patterns.md#ban-04` |
174
+
175
+ ---
176
+
177
+ ## Benchmark Citations
178
+
179
+ | Claim | Sources |
180
+ |-------|---------|
181
+ | scope="col" on all th headers | WAI-ARIA, Carbon DataTable, Polaris |
182
+ | aria-sort on sortable columns | WAI-ARIA APG grid pattern, Carbon, Atlassian |
183
+ | aria-selected="true" on selected rows | WAI-ARIA APG, Carbon, Ant |
184
+ | role="grid" for interactive cell navigation | WAI-ARIA APG, Carbon |
185
+ | Virtualise at > 200 rows | Carbon, Ant (TanStack Virtual recommendation) |
186
+
187
+ Full system URLs: `connections/design-corpora.md`
188
+
189
+ ---
190
+
191
+ ## Grep Signatures
192
+
193
+ ```bash
194
+ # <th> missing scope attribute
195
+ grep -rn '<th' src/ | grep -v 'scope='
196
+
197
+ # Sortable column missing aria-sort
198
+ grep -rn 'sortable\|sort-header\|on.*sort' src/ | grep '<th' | grep -v 'aria-sort'
199
+
200
+ # Selected row missing aria-selected
201
+ grep -rn 'row.*selected\|selected.*row\|isSelected' src/ | grep '<tr' | grep -v 'aria-selected'
202
+
203
+ # Table built with divs (no semantic markup)
204
+ grep -rn 'class.*table\|data-table' src/ | grep '<div' | grep -v 'role="table"\|role="grid"'
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Failing Example
210
+
211
+ ```html
212
+ <!-- BAD: <div> table with no semantic markup and no ARIA grid roles -->
213
+ <div class="data-table">
214
+ <div class="table-header">
215
+ <div class="col-header" onclick="sortByName()">Name</div> <!-- no aria-sort -->
216
+ <div class="col-header">Status</div>
217
+ <div class="col-header">Amount</div>
218
+ </div>
219
+ <div class="table-row selected"> <!-- no aria-selected -->
220
+ <div class="cell">Alice Chen</div>
221
+ <div class="cell">Active</div>
222
+ <div class="cell">$1,200.00</div>
223
+ </div>
224
+ </div>
225
+ ```
226
+
227
+ **Why it fails**: No `<table>/<thead>/<tbody>/<th>/<td>` semantics; no `scope="col"` on headers; no `aria-sort` on sortable column; `selected` row uses CSS class without `aria-selected="true"`; screen readers cannot navigate by row/column; no caption/label.
228
+ **Grep detection**: `grep -rn 'data-table\|table.*container' src/ | grep '<div' | grep -v 'role='`
229
+ **Fix**: Replace with semantic `<table>` and `<th scope="col">` headers; add `aria-sort` to sortable headers; add `aria-selected="true"` to selected `<tr>`; add `<caption>` describing the table.