@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,210 @@
1
+ # Progress — Benchmark Spec
2
+
3
+ **Harvested from**: Material 3, Carbon (ProgressIndicator), Polaris, Mantine
4
+ **Wave**: 3 · **Category**: Feedback
5
+
6
+ ---
7
+
8
+ ## Purpose
9
+
10
+ A progress indicator communicates the status of an ongoing operation. Determinate variants show a known percentage of completion (0–100%); indeterminate variants signal ongoing work when duration is unknown. Linear bars are suited to step-based or file-based progress; circular (spinner-ring) variants are suited to inline or compact contexts. *(Material 3, Carbon, Polaris, Mantine agree: separate determinate vs. indeterminate; linear vs. circular)*
11
+
12
+ ---
13
+
14
+ ## Anatomy
15
+
16
+ **Linear**
17
+ ```
18
+ [track ──────────────────────────────────]
19
+ [fill ◼◼◼◼◼◼◼◼◼◼◼◼◼◼·····················]
20
+ ↑ role="progressbar" aria-valuenow="45"
21
+ ```
22
+
23
+ **Circular**
24
+ ```
25
+ ╭──╮
26
+ ╭ ╮
27
+ ╰ ╯ ← SVG stroke-dashoffset ring
28
+ ╰──╯
29
+ role="progressbar"
30
+ ```
31
+
32
+ | Part | Required | Notes |
33
+ |------|----------|-------|
34
+ | Track | Yes | Background rail (linear) or ring background (circular) |
35
+ | Fill / indicator | Yes | Foreground showing progress amount |
36
+ | Label (visually hidden ok) | Yes | `aria-label` or `aria-labelledby` — describes what is loading |
37
+ | Value text | No | Rendered percentage (e.g. "45%") — supplement to ARIA value |
38
+
39
+ ---
40
+
41
+ ## Variants
42
+
43
+ | Variant | Description | Systems |
44
+ |---------|-------------|---------|
45
+ | Linear determinate | Bar fills left-to-right proportionally | Material 3, Carbon, Polaris, Mantine |
46
+ | Linear indeterminate | Bar animates in loop (shimmer or sweep) | Material 3, Carbon, Polaris, Mantine |
47
+ | Circular determinate | SVG ring fills by stroke-dashoffset | Material 3, Carbon, Mantine |
48
+ | Circular indeterminate | SVG ring rotates in loop | Material 3, Carbon, Mantine |
49
+
50
+ **Norm** (≥4/18 systems agree): `role="progressbar"` on all variants; `aria-valuenow` only on determinate; `aria-valuemin=0` + `aria-valuemax=100` always.
51
+ **Diverge**: Polaris calls the circular variant "Spinner" (single indeterminate state only); Material 3 distinguishes "linear progress indicator" and "circular progress indicator" as separate component families; Carbon offers multi-step linear progress ("ProgressStep") as a distinct component.
52
+
53
+ ---
54
+
55
+ ## States
56
+
57
+ | State | Trigger | Visual | ARIA |
58
+ |-------|---------|--------|------|
59
+ | determinate (0–100%) | known progress value | Fill width/stroke = percentage | `aria-valuenow={n}` |
60
+ | indeterminate | unknown duration | Looping animation | `aria-valuetext="Loading"` |
61
+ | complete | value reaches 100% | Full fill; brief hold before removal | `aria-valuenow="100"` |
62
+ | paused | operation suspended | Static fill; muted color | `aria-valuetext="Paused"` |
63
+
64
+ ---
65
+
66
+ ## Sizing & Spacing
67
+
68
+ **Linear**
69
+
70
+ | Size | Height | Notes |
71
+ |------|--------|-------|
72
+ | sm | 4px (default) | Decorative; thin above content |
73
+ | md | 8px | Accessible minimum — recommended when bar is the primary indicator |
74
+ | lg | 12px | High-emphasis; file upload, step progress |
75
+
76
+ **Circular**
77
+
78
+ | Size | Diameter | Stroke width | Notes |
79
+ |------|----------|-------------|-------|
80
+ | sm | 20px | 2px | Inline within text/button |
81
+ | md | 32px | 3px | Component-level loading |
82
+ | lg | 48px | 4px | Page/section loading |
83
+
84
+ **Norm**: 4px linear height default (Material 3); 8px recommended for standalone accessibility *(Carbon)*; circular diameter 20–48px *(Material 3, Mantine)*.
85
+
86
+ ---
87
+
88
+ ## Typography
89
+
90
+ - Value label (if shown): numeric-sm (12px/tabular-nums) — percentage readability
91
+ - Associated label (if visible): body-sm (14px/400) — describes what is loading
92
+ - Do not truncate the associated label; use a visually-hidden version if space is constrained
93
+
94
+ Cross-link: `reference/typography.md` — tabular-nums for percentage values
95
+
96
+ ---
97
+
98
+ ## Keyboard & Accessibility
99
+
100
+ > **WAI-ARIA role**: `progressbar`
101
+ > **Required attributes**: `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-label` or `aria-labelledby`; `aria-valuenow` on determinate only
102
+
103
+ ### Keyboard Contract
104
+
105
+ *Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/patterns/meter/ — W3C — 2024*
106
+
107
+ | Key | Action |
108
+ |-----|--------|
109
+ | (none) | Progress bar is not interactive; no keyboard interaction required |
110
+
111
+ Progress indicators are read-only status elements. They receive no keyboard focus unless embedded in a larger focusable region.
112
+
113
+ ### Accessibility Rules
114
+
115
+ - `aria-label` or `aria-labelledby` MUST describe what is loading (e.g. "Uploading file", "Loading results") — a bare `role="progressbar"` with no label is announced as empty *(WAI-ARIA APG)*
116
+ - Determinate bars MUST include `aria-valuenow` matching the current integer percentage *(WAI-ARIA APG)*
117
+ - Indeterminate bars MUST omit `aria-valuenow` and instead set `aria-valuetext="Loading"` or similar *(WAI-ARIA APG)*
118
+ - `aria-valuemin` and `aria-valuemax` MUST be present on all progress bars (default 0 and 100)
119
+ - Indeterminate animation MUST respect `prefers-reduced-motion` — reduce to opacity pulse or static indicator *(WCAG 2.3.3)*
120
+ - Color contrast of fill vs. track MUST meet 3:1 minimum for non-text UI components *(WCAG 1.4.11)*
121
+
122
+ Cross-link: `reference/accessibility.md` — `prefers-reduced-motion`, WCAG 1.4.11
123
+
124
+ ---
125
+
126
+ ## Motion
127
+
128
+ | Transition | Duration | Easing | Notes |
129
+ |------------|----------|--------|-------|
130
+ | Determinate fill advance | 300ms | ease-out | Smooth value update on change |
131
+ | Indeterminate linear sweep | 1.2s | ease-in-out | Infinite loop; reverse direction at 50% |
132
+ | Circular spin | 1.2s | linear | Single full rotation per cycle |
133
+ | Complete → remove | 400ms | ease-in | Brief hold at 100% then fade/collapse |
134
+
135
+ **BAN**: Bouncing or elasticity on indeterminate loop — communicates false progress rhythm. Do not use `transition: all` (catches color changes during theme swap).
136
+
137
+ Cross-link: `reference/motion.md` — `prefers-reduced-motion`: replace sweep with opacity 0.5→1 pulse
138
+
139
+ ---
140
+
141
+ ## Do / Don't
142
+
143
+ ### Do
144
+ - Always provide `aria-label` describing what is loading *(WAI-ARIA APG)*
145
+ - Use `aria-valuenow` on determinate variants and omit on indeterminate *(WAI-ARIA APG)*
146
+ - Use 8px+ height for standalone linear bars — 4px bars lack sufficient touch and visual target *(Carbon)*
147
+ - Transition fill smoothly (300ms ease-out) when value updates *(Material 3, Mantine)*
148
+
149
+ ### Don't
150
+ - Don't use `aria-valuenow` on indeterminate bars — it implies a known value *(WAI-ARIA APG)*
151
+ - Don't show a spinner (circular indeterminate) when content shape is known — use Skeleton instead *(Carbon, Polaris)*
152
+ - Don't remove the progress bar the instant it hits 100% — hold briefly so the completion is registered *(Material 3)*
153
+ - Don't animate with infinite bounce — implies bouncing progress rhythm *(Carbon, Mantine)*
154
+
155
+ ---
156
+
157
+ ## Anti-patterns Cross-links
158
+
159
+ | Anti-pattern | Entry |
160
+ |--------------|-------|
161
+ | Indeterminate bar with aria-valuenow | `reference/anti-patterns.md#ban-aria-value` |
162
+ | Spinner used when content shape is known | `reference/anti-patterns.md#ban-spinner-overuse` |
163
+
164
+ ---
165
+
166
+ ## Benchmark Citations
167
+
168
+ | Claim | Sources |
169
+ |-------|---------|
170
+ | role="progressbar" on all variants | WAI-ARIA APG |
171
+ | aria-valuenow only on determinate | WAI-ARIA APG, Material 3, Carbon |
172
+ | aria-label required (what is loading) | WAI-ARIA APG |
173
+ | 8px accessible minimum height | Carbon |
174
+ | 1.2s animation loop duration | Material 3, Mantine |
175
+ | Respect prefers-reduced-motion | WCAG 2.3.3 |
176
+
177
+ Full system URLs: `connections/design-corpora.md`
178
+
179
+ ---
180
+
181
+ ## Grep Signatures
182
+
183
+ ```bash
184
+ # Progress bar missing aria-label or aria-labelledby
185
+ grep -rn 'role="progressbar"' src/ | grep -v 'aria-label\|aria-labelledby'
186
+
187
+ # Determinate progress missing aria-valuenow
188
+ grep -rn 'role="progressbar"' src/ | grep -v 'aria-valuenow\|indeterminate'
189
+
190
+ # Progress missing valuemin/valuemax
191
+ grep -rn 'role="progressbar"' src/ | grep -v 'aria-valuemin\|aria-valuemax'
192
+
193
+ # Indeterminate with aria-valuenow (invalid pattern)
194
+ grep -rn 'indeterminate' src/ | grep 'aria-valuenow'
195
+ ```
196
+
197
+ ---
198
+
199
+ ## Failing Example
200
+
201
+ ```html
202
+ <!-- BAD: progress bar with no accessible label and no value attributes -->
203
+ <div class="progress-bar">
204
+ <div class="progress-bar__fill" style="width: 45%"></div>
205
+ </div>
206
+ ```
207
+
208
+ **Why it fails**: No `role="progressbar"` so screen readers do not recognize this as a progress indicator. No `aria-label` so there is no description of what is loading. No `aria-valuenow`, `aria-valuemin`, or `aria-valuemax` so screen readers cannot read the percentage even if the role were present.
209
+ **Grep detection**: `grep -rn 'progress-bar\|progressBar\|progress__fill' src/ | grep -v 'role="progressbar"'`
210
+ **Fix**: `<div role="progressbar" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" aria-label="Uploading file"><div style="width:45%"></div></div>`
@@ -0,0 +1,226 @@
1
+ # Rich-Text Editor — Benchmark Spec
2
+
3
+ **Harvested from**: Tiptap/ProseMirror, Lexical (Meta), Slate.js, Atlassian Fabric Editor
4
+ **Wave**: 5 · **Category**: Advanced
5
+ **Spec file**: `reference/components/rich-text-editor.md`
6
+
7
+ ---
8
+
9
+ ## Purpose
10
+
11
+ A Rich-Text Editor (RTE) provides WYSIWYG content authoring with inline formatting (bold, italic, links), block-level elements (headings, lists, blockquotes), and optional advanced features (mentions, embeds, tables). It is appropriate for long-form content where plain `<textarea>` is insufficient and a full CMS-style document editor is overkill. *(Tiptap, Lexical, Atlassian agree: contenteditable + ProseMirror/Lexical model + explicit toolbar is the production-grade RTE pattern.)*
12
+
13
+ ---
14
+
15
+ ## Anatomy
16
+
17
+ ```
18
+ ┌── role="toolbar" aria-label="Text formatting" ──────────────────┐
19
+ │ [B] [I] [U] [S] │ [H1][H2] │ [• List][1. List] │ [Link][Image] │
20
+ └──────────────────────────────────────────────────────────────────┘
21
+ ┌── role="textbox" aria-multiline="true" aria-label="Post body" ──┐
22
+ │ │
23
+ │ Start typing here… (placeholder via CSS ::before) │
24
+ │ │
25
+ └──────────────────────────────────────────────────────────────────┘
26
+ ```
27
+
28
+ | Part | Required | Notes |
29
+ |------|----------|-------|
30
+ | Editable area | Yes | `contenteditable="true"` + `role="textbox"` + `aria-multiline="true"` |
31
+ | Toolbar | Yes (for formatting) | `role="toolbar"` + `aria-label`; groups via `role="group"` |
32
+ | Toolbar buttons | Yes | `role="button"`; toggle buttons add `aria-pressed` |
33
+ | Placeholder | No | CSS `[data-placeholder]::before` — NOT HTML attribute |
34
+ | Mention list | No | `role="listbox"` floating suggestion list triggered by `@` |
35
+ | Character count | No | `aria-live="polite"` updated region |
36
+ | Read-only overlay | No | `contenteditable="false"` + `aria-readonly="true"` |
37
+
38
+ ---
39
+
40
+ ## Variants
41
+
42
+ | Variant | Description | Systems |
43
+ |---------|-------------|---------|
44
+ | Minimal | Bold/italic/underline + link only | Tiptap (StarterKit minimal), Atlassian (inline comment) |
45
+ | Standard | Full paragraph formatting + lists + links | Tiptap, Lexical, Slate |
46
+ | Document | Full editor with headings, tables, embeds, mentions | Atlassian Fabric Editor, Tiptap (full), Lexical (full) |
47
+ | Read-only | Rendered content; no editing | All systems |
48
+ | Bubble toolbar | Formatting toolbar appears on text selection (tooltip style) | Tiptap, Atlassian inline editor |
49
+
50
+ **Norm** (≥3/4 systems agree): toolbar at top or on selection; contenteditable with explicit role="textbox"; keyboard shortcuts for core formatting.
51
+ **Diverge**: Atlassian uses a floating toolbar on selection; Tiptap supports both fixed and bubble modes; Lexical uses a plugin architecture; Slate treats everything as React tree nodes.
52
+
53
+ ---
54
+
55
+ ## States
56
+
57
+ | State | Trigger | Visual | ARIA |
58
+ |-------|---------|--------|------|
59
+ | default | — | Cursor; toolbar buttons at rest | — |
60
+ | focused | Click or Tab into editable area | Focus ring on editable container | — |
61
+ | toolbar button active | Text with formatting selected | `aria-pressed="true"` on toggle button | `aria-pressed="true"` |
62
+ | placeholder | No content in editable area | Placeholder text via CSS ::before | — |
63
+ | read-only | `readOnly` prop | No cursor change; grayed toolbar | `aria-readonly="true"`, `contenteditable="false"` |
64
+ | disabled | `disabled` prop | 38% opacity; no interaction | `aria-disabled="true"` |
65
+ | mention-open | `@` typed | Floating listbox appears | `role="listbox"` visible |
66
+ | error | Validation failure | Red border; error message below | `aria-describedby` → error message |
67
+
68
+ ---
69
+
70
+ ## Sizing & Spacing
71
+
72
+ | Size | Min Height | Toolbar Height | Font |
73
+ |------|-----------|----------------|------|
74
+ | sm | 80px | 32px | 13px |
75
+ | md (default) | 160px | 40px | 14px |
76
+ | lg | 320px | 48px | 16px |
77
+
78
+ **Norm**: Editor area grows with content (auto-height); enforce max-height with overflow scroll if needed. Toolbar buttons follow button-sm/md sizing with 8px gaps between groups *(Atlassian, Tiptap)*.
79
+
80
+ Cross-link: `reference/surfaces.md` — toolbar button hit targets.
81
+
82
+ ---
83
+
84
+ ## Typography
85
+
86
+ - Editor content: body-md, line-height 1.6 for readable long-form text
87
+ - Heading 1: 2em; Heading 2: 1.5em; Heading 3: 1.25em — relative to editor base font
88
+ - Code blocks: monospace, 0.9em, background token surface-code
89
+ - Placeholder text: same size/font as body, secondary color via CSS
90
+
91
+ Cross-link: `reference/typography.md` — heading scale, code font stack.
92
+
93
+ ---
94
+
95
+ ## Keyboard & Accessibility
96
+
97
+ > **WAI-ARIA role**: `textbox` (editable area), `toolbar` (toolbar container), `group` (toolbar sections), `button` (toolbar actions), `listbox` (mention suggestions)
98
+ > **Required attributes**: `role="textbox"` + `aria-multiline="true"` + `aria-label` or `aria-labelledby` on editable area; `aria-pressed` on toggle toolbar buttons; `role="toolbar"` + `aria-label` on toolbar
99
+
100
+ ### Keyboard Contract
101
+
102
+ *Derived from WAI-ARIA APG toolbar pattern — https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/ — W3C — 2024, and standard contenteditable browser behavior*
103
+
104
+ | Key | Action |
105
+ |-----|--------|
106
+ | Tab | Move focus into the editable area from outside; move out of editor |
107
+ | Ctrl/Cmd + B | Toggle bold on selected text |
108
+ | Ctrl/Cmd + I | Toggle italic on selected text |
109
+ | Ctrl/Cmd + U | Toggle underline on selected text |
110
+ | Ctrl/Cmd + Z | Undo last action |
111
+ | Ctrl/Cmd + Shift + Z | Redo |
112
+ | Ctrl/Cmd + K | Insert or edit link |
113
+ | @ (in editor) | Trigger mention suggestion list |
114
+ | Arrow keys (in listbox) | Navigate mention suggestions |
115
+ | Enter / Space (in listbox) | Select mention suggestion |
116
+ | Escape (in listbox) | Dismiss mention list |
117
+ | F10 or Alt + F10 | Move focus from editable area to toolbar (accessibility shortcut) |
118
+ | Arrow keys (in toolbar) | Move between toolbar buttons (roving tabindex) |
119
+ | Home / End (in toolbar) | First / last toolbar button |
120
+
121
+ ### Accessibility Rules
122
+
123
+ - Editable area MUST have `role="textbox"` — bare `contenteditable` is announced as a generic region by most AT
124
+ - `aria-multiline="true"` MUST be set — announces correct Enter-key behavior to screen readers
125
+ - Toolbar toggle buttons (bold, italic, lists) MUST use `aria-pressed="true/false"` to reflect current selection state
126
+ - Placeholder MUST use CSS `[data-placeholder]::before` content — not a `placeholder` HTML attribute on contenteditable (screen readers read it incorrectly or not at all)
127
+ - Keyboard shortcuts MUST be documented in a tooltip or help section — not assumed
128
+ - Read-only: set both `contenteditable="false"` AND `aria-readonly="true"` — contenteditable="false" alone is insufficient for AT
129
+ - Mention listbox MUST use `role="listbox"` with `role="option"` items; focused option uses `aria-selected="true"`
130
+
131
+ Cross-link: `reference/accessibility.md` — contenteditable accessibility, toolbar pattern.
132
+
133
+ ---
134
+
135
+ ## Motion
136
+
137
+ | Transition | Duration | Easing | Notes |
138
+ |------------|----------|--------|-------|
139
+ | Toolbar button active state | 80ms | ease-out | Background fill on aria-pressed change |
140
+ | Mention list open | 150ms | ease-out | Fade + slide up from caret position |
141
+ | Mention list dismiss | 100ms | ease-in | Fade out |
142
+ | Bubble toolbar appear | 150ms | ease-out | Fade in above selection |
143
+ | Bubble toolbar dismiss | 80ms | ease-in | Fade out on deselect |
144
+
145
+ **BAN**: Do not animate text reflow on formatting changes — typing performance is critical; any CSS transition on editor content causes visual jitter.
146
+
147
+ Cross-link: `reference/motion.md` — reduced-motion: remove mention list slide; keep instant formatting toggle.
148
+
149
+ ---
150
+
151
+ ## Do / Don't
152
+
153
+ ### Do
154
+ - Add `role="textbox"` + `aria-multiline="true"` to the contenteditable element *(WAI-ARIA APG)*
155
+ - Use `aria-pressed` on all toggle toolbar buttons (bold, italic, list toggles) *(WAI-ARIA APG toolbar pattern)*
156
+ - Implement placeholder via CSS `::before` pseudo-element, not HTML attribute *(Tiptap, Atlassian)*
157
+ - Document keyboard shortcuts in a tooltip or accessible help dialog *(Atlassian Fabric Editor)*
158
+
159
+ ### Don't
160
+ - Don't use bare `contenteditable` without `role="textbox"` — AT announces it as a generic landmark *(diverges from all 4 systems)*
161
+ - Don't omit `aria-pressed` on toggle buttons — AT users cannot determine current formatting state *(WAI-ARIA APG)*
162
+ - Don't use a `placeholder` HTML attribute on contenteditable — it is not a valid attribute and screen reader behavior is undefined *(MDN, Tiptap docs)*
163
+ - Don't suppress `outline` on the editable area without a custom focus-visible ring *(WCAG 2.4.7)*
164
+
165
+ ---
166
+
167
+ ## Anti-patterns Cross-links
168
+
169
+ | Anti-pattern | Entry |
170
+ |--------------|-------|
171
+ | BAN-06 | contenteditable without role="textbox" — `reference/anti-patterns.md#ban-06` |
172
+ | BAN-04 | transition: all on editor content causing reflow jank — `reference/anti-patterns.md#ban-04` |
173
+
174
+ ---
175
+
176
+ ## Benchmark Citations
177
+
178
+ | Claim | Sources |
179
+ |-------|---------|
180
+ | contenteditable needs role="textbox" + aria-multiline="true" | WAI-ARIA spec, Tiptap a11y guide, Atlassian |
181
+ | Toolbar toggle buttons need aria-pressed | WAI-ARIA APG toolbar pattern |
182
+ | Placeholder via CSS ::before, not HTML attribute | Tiptap docs, Atlassian Fabric Editor |
183
+ | Mention list uses role="listbox" + role="option" | Tiptap mention extension, Lexical mention plugin |
184
+ | Ctrl/Cmd+B/I/U keyboard shortcuts are standard | Lexical, Tiptap, Slate — all implement these |
185
+
186
+ Full system URLs: `connections/design-corpora.md`
187
+
188
+ ---
189
+
190
+ ## Grep Signatures
191
+
192
+ ```bash
193
+ # contenteditable element missing role="textbox" (announced as generic region)
194
+ grep -rn 'contenteditable="true"' src/ | grep -v 'role="textbox"'
195
+
196
+ # Toolbar toggle buttons missing aria-pressed
197
+ grep -rn 'role="toolbar"' src/ -A 50 | grep 'role="button"' | grep -v 'aria-pressed'
198
+
199
+ # Placeholder set as HTML attribute on contenteditable (invalid)
200
+ grep -rn 'contenteditable' src/ | grep 'placeholder='
201
+
202
+ # Mention listbox missing role="listbox"
203
+ grep -rn 'mention\|@mention\|MentionList' src/ | grep -v 'role="listbox"'
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Failing Example
209
+
210
+ ```html
211
+ <!-- BAD: rich-text area using only contenteditable without role="textbox" -->
212
+ <div
213
+ contenteditable="true"
214
+ class="editor"
215
+ placeholder="Start writing..."
216
+ >
217
+ </div>
218
+ <div class="toolbar">
219
+ <button onclick="document.execCommand('bold')">B</button>
220
+ <button onclick="document.execCommand('italic')">I</button>
221
+ </div>
222
+ ```
223
+
224
+ **Why it fails**: (1) Bare `contenteditable` is announced by most AT as a generic region, not a text input — users do not know they can type. (2) `placeholder` is not a valid HTML attribute on `<div>` — screen readers do not announce it. (3) Toolbar buttons have no `aria-pressed` — AT cannot determine if bold/italic is currently active. (4) `document.execCommand` is deprecated.
225
+ **Grep detection**: `grep -rn 'contenteditable="true"' src/ | grep -v 'role="textbox"'`
226
+ **Fix**: Add `role="textbox"` + `aria-multiline="true"` + `aria-label="Post body"` to the editable div; implement placeholder via CSS `[data-empty]::before { content: attr(data-placeholder) }`; add `aria-pressed="true/false"` to toolbar toggle buttons; use a modern editor library (Tiptap, Lexical) instead of execCommand.
@@ -0,0 +1,211 @@
1
+ # Sidebar (Collapsible Side Navigation Panel) — Benchmark Spec
2
+
3
+ **Harvested from**: Material 3, Carbon, Polaris, Atlassian, UUPM (app-interface, MIT)
4
+ **Wave**: 4 · **Category**: Navigation
5
+
6
+ ---
7
+
8
+ ## Purpose
9
+
10
+ A sidebar provides persistent or collapsible secondary navigation along the vertical axis of an application. In expanded state it shows icon + label; in collapsed state it shows icon only (with a tooltip). It is distinct from a Drawer (which is a modal overlay — see `drawer.md`) and from a Navbar (primary horizontal navigation). Use a sidebar for application-level section switching and hierarchical settings navigation. *(Material 3 Navigation Drawer, Carbon UI Shell Left Nav, Atlassian SideNavigation, Polaris Navigation agree)*
11
+
12
+ ---
13
+
14
+ ## Anatomy
15
+
16
+ ```
17
+ ┌─────────────────┐ ┌────┐
18
+ │ [≡] App Name │ ◄──► │[≡] │ collapsed (icon-only)
19
+ │─────────────────│ │────│
20
+ │ 🏠 Dashboard │ │[🏠]│ tooltip: "Dashboard"
21
+ │ 📊 Analytics │ │[📊]│
22
+ │ ▾ Settings │ │[⚙] │
23
+ │ Account │ └────┘
24
+ │ Privacy │
25
+ │ Security │
26
+ └─────────────────┘
27
+ ```
28
+
29
+ | Part | Required | Notes |
30
+ |------|----------|-------|
31
+ | `<nav>` wrapper | Yes | `role="navigation"` + `aria-label="Secondary"` |
32
+ | Toggle button | Yes | `aria-expanded` + `aria-controls` pointing to nav |
33
+ | Nav item | Yes | `<a>` for routing; `<button>` for non-routing actions |
34
+ | Sub-section toggle | No | `<button>` with `aria-expanded`; chevron icon rotates |
35
+ | Sub-section items | No | Indented child items; hidden when parent collapsed |
36
+ | Tooltip | Conditional | On icon-only items in collapsed state; `role="tooltip"` |
37
+ | Active indicator | Yes | `aria-current="page"` on active item |
38
+
39
+ ---
40
+
41
+ ## Variants
42
+
43
+ | Variant | Description | Systems |
44
+ |---------|-------------|---------|
45
+ | Expanded | Icon + label visible; full width (240–280px) | All systems |
46
+ | Collapsed / mini | Icon only; 48–64px wide; tooltips on hover/focus | Material 3, Carbon, Atlassian |
47
+ | Floating / overlay | Overlay on top of content (mobile drawer pattern) | Material 3, Polaris |
48
+ | Settings nav | Category tree: Settings › Account › Privacy › Security | UUPM app-interface (MIT) |
49
+ | Dashboard nav | Section switcher: Dashboard / Analytics / Users / Settings | UUPM app-interface (MIT) |
50
+
51
+ **Norm** (≥4 systems agree): expanded width 240–280px; collapsed width 48–64px; toggle button at top or bottom.
52
+ **Diverge**: Carbon collapses to a rail (16px) with hover-expand; Material 3 differentiates between NavigationDrawer (permanent) and ModalNavigationDrawer (mobile).
53
+
54
+ ---
55
+
56
+ ## States
57
+
58
+ | State | Trigger | Visual | ARIA |
59
+ |-------|---------|--------|------|
60
+ | expanded | default or toggle | Full width, labels visible | `aria-expanded="true"` on toggle |
61
+ | collapsed | toggle click | Icon-only, labels hidden | `aria-expanded="false"` on toggle |
62
+ | item-default | — | Resting fill | — |
63
+ | item-hover | pointer over | 8% overlay | — |
64
+ | item-focus | keyboard focus | 2px focus-visible ring | — |
65
+ | item-active | current route | Filled pill or left indicator bar | `aria-current="page"` |
66
+ | subsection-open | button click | Children visible; chevron rotated 180° | `aria-expanded="true"` on section button |
67
+ | subsection-closed | button click | Children hidden | `aria-expanded="false"` on section button |
68
+
69
+ ---
70
+
71
+ ## Sizing & Spacing
72
+
73
+ | State | Width | Item height | Item padding H |
74
+ |-------|-------|-------------|----------------|
75
+ | Expanded | 240–280px | 40px | 16px |
76
+ | Collapsed | 48–64px | 40px | 12px (centered icon) |
77
+ | Sub-item indent | — | 36px | 32px (16px base + 16px indent) |
78
+
79
+ **Norm**: 240px expanded width (Carbon, Atlassian, Polaris). 40px item height matches button defaults.
80
+
81
+ ---
82
+
83
+ ## Typography
84
+
85
+ - Nav item label: body-sm (13–14px), weight 400; active item weight 500 or 600
86
+ - Sub-section heading (if present): label-xs (11–12px), uppercase, weight 600, `color: --text-subtle`
87
+ - Tooltip: body-xs (11–12px) — concise, matches item label exactly in collapsed state
88
+ - Never truncate nav item labels — resize the sidebar or abbreviate the label intentionally
89
+
90
+ Cross-link: `reference/typography.md` — label scale, body-sm
91
+
92
+ ---
93
+
94
+ ## Keyboard & Accessibility
95
+
96
+ > **WAI-ARIA role**: `navigation` (wrapper)
97
+ > **Required attributes**: `aria-label="Secondary"` on `<nav>`; `aria-expanded` on toggle button and collapsible section buttons; `aria-current="page"` on active item
98
+
99
+ ### Keyboard Contract
100
+
101
+ *Quoted verbatim from WAI-ARIA APG — https://www.w3.org/WAI/ARIA/apg/patterns/disclosure/ — W3C — 2024*
102
+
103
+ | Key | Action |
104
+ |-----|--------|
105
+ | Tab / Shift+Tab | Moves focus through focusable items in DOM order |
106
+ | Enter / Space | Activates focused link (navigates) or button (toggles section/sidebar) |
107
+ | ArrowDown | Moves focus to next visible nav item (optional enhancement) |
108
+ | ArrowUp | Moves focus to previous visible nav item (optional enhancement) |
109
+ | Escape | Closes mobile overlay sidebar; returns focus to toggle |
110
+
111
+ ### Accessibility Rules
112
+
113
+ - `<nav>` MUST have `aria-label="Secondary"` to distinguish from the primary navbar landmark
114
+ - Every collapsible sub-section MUST have `aria-expanded` on its toggle button
115
+ - Items that navigate (routing) MUST be `<a href>` links; items that only toggle state MUST be `<button>` (not `<a href="#">`)
116
+ - In collapsed state, each icon-only item MUST have a tooltip AND `aria-label` (tooltip is not a substitute for accessible name)
117
+ - `aria-current="page"` MUST be present on the currently active item
118
+
119
+ Cross-link: `reference/accessibility.md` — landmark labelling, disclosure pattern
120
+
121
+ ---
122
+
123
+ ## Motion
124
+
125
+ | Transition | Duration | Easing | Notes |
126
+ |------------|----------|--------|-------|
127
+ | Expand / collapse sidebar | 200ms | ease-in-out | Width transition; respect `prefers-reduced-motion` |
128
+ | Sub-section open | 150ms | ease-out | Height expand; `overflow: hidden` clip |
129
+ | Sub-section close | 120ms | ease-in | Height collapse |
130
+ | Chevron rotate | 150ms | ease-in-out | 0° → 180° on open |
131
+ | Label fade in | 100ms | ease-out | Delay until width ≥ 140px to prevent overlap |
132
+
133
+ **BAN**: Animating sidebar width using `transition: all` — catches unrelated property changes and causes jank.
134
+
135
+ Cross-link: `reference/motion.md` — layout transitions, BAN-04
136
+
137
+ ---
138
+
139
+ ## Do / Don't
140
+
141
+ ### Do
142
+ - Use `<a href>` for routing nav items and `<button>` for non-routing actions *(Carbon, WAI-ARIA APG)*
143
+ - Label the `<nav>` with `aria-label="Secondary"` *(WAI-ARIA APG landmark regions)*
144
+ - Show tooltips on icon-only items in collapsed state *(Material 3, Carbon, Atlassian)*
145
+ - Use `aria-expanded` on sub-section toggles *(WAI-ARIA APG disclosure pattern)*
146
+
147
+ ### Don't
148
+ - Don't use `<a href="#">` for items that don't navigate — breaks bookmark/open-in-new-tab expectations *(Carbon, WAI-ARIA)*
149
+ - Don't hide sub-items with `display: none` without also removing them from tab order *(WCAG 2.1.1)*
150
+ - Don't use the same `aria-label` on both sidebar nav and top navbar *(WAI-ARIA landmark uniqueness)*
151
+ - Don't collapse the sidebar below 44px touch target width on mobile *(WCAG 2.5.5)*
152
+
153
+ ---
154
+
155
+ ## Anti-patterns Cross-links
156
+
157
+ | Anti-pattern | Entry |
158
+ |--------------|-------|
159
+ | BAN-04 | `transition: all` on interactive elements — `reference/anti-patterns.md#ban-04` |
160
+
161
+ ---
162
+
163
+ ## Benchmark Citations
164
+
165
+ | Claim | Sources |
166
+ |-------|---------|
167
+ | aria-label="Secondary" distinct from Primary | WAI-ARIA APG landmark regions, WCAG 4.1.2 |
168
+ | `<button>` for non-routing, `<a>` for routing | Carbon, WAI-ARIA APG, Primer |
169
+ | aria-expanded on collapsible sections | WAI-ARIA APG disclosure pattern |
170
+ | 240px expanded / 48–64px collapsed widths | Carbon, Atlassian, Polaris |
171
+ | Tooltip on icon-only collapsed items | Material 3, Carbon, Atlassian |
172
+
173
+ Full system URLs: `connections/design-corpora.md`
174
+
175
+ ---
176
+
177
+ ## Grep Signatures
178
+
179
+ ```bash
180
+ # nav element missing aria-label
181
+ grep -rn '<nav' src/ | grep -v 'aria-label\|aria-labelledby'
182
+
183
+ # Collapsible section missing aria-expanded
184
+ grep -rn 'sidebar\|sidenav\|side-nav' src/ | grep 'collapsible\|expandable\|toggle' | grep -v 'aria-expanded'
185
+
186
+ # href="#" on nav items (non-routing anchor misuse)
187
+ grep -rn 'href="#"' src/ | grep -i 'nav\|sidebar\|menu'
188
+
189
+ # Active item missing aria-current
190
+ grep -rn 'class.*active\|isActive\|is-active' src/ | grep -i 'nav.*item\|sidebar.*item' | grep -v 'aria-current'
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Failing Example
196
+
197
+ ```html
198
+ <!-- BAD: sidebar items using <a href="#"> for non-routing actions -->
199
+ <nav> <!-- missing aria-label -->
200
+ <a href="#">Dashboard</a>
201
+ <a href="#">Analytics</a>
202
+ <a href="#" class="active">Settings</a> <!-- no aria-current -->
203
+ <a href="#">
204
+ Account <!-- subsection, but no aria-expanded -->
205
+ </a>
206
+ </nav>
207
+ ```
208
+
209
+ **Why it fails**: `<nav>` has no label (ambiguous landmark); `<a href="#">` creates false navigation expectations and adds to browser history; active state uses CSS class only; no `aria-current="page"`; no `aria-expanded` for the sub-section.
210
+ **Grep detection**: `grep -rn 'href="#"' src/ | grep -i 'nav\|sidebar'`
211
+ **Fix**: Replace `<a href="#">` with `<button>` for non-routing items; add `aria-label="Secondary"` to `<nav>`; add `aria-current="page"` to active item; add `aria-expanded` to sub-section toggles.