pi-generative-ui 0.1.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 (23) hide show
  1. package/.pi/extensions/generative-ui/claude-guidelines/CORE.md +89 -0
  2. package/.pi/extensions/generative-ui/claude-guidelines/art.md +175 -0
  3. package/.pi/extensions/generative-ui/claude-guidelines/art_interactive.md +297 -0
  4. package/.pi/extensions/generative-ui/claude-guidelines/chart.md +255 -0
  5. package/.pi/extensions/generative-ui/claude-guidelines/chart_interactive.md +255 -0
  6. package/.pi/extensions/generative-ui/claude-guidelines/diagram.md +624 -0
  7. package/.pi/extensions/generative-ui/claude-guidelines/interactive.md +209 -0
  8. package/.pi/extensions/generative-ui/claude-guidelines/mockup.md +209 -0
  9. package/.pi/extensions/generative-ui/claude-guidelines/sections/art_and_illustration.md +11 -0
  10. package/.pi/extensions/generative-ui/claude-guidelines/sections/charts_chart_js.md +43 -0
  11. package/.pi/extensions/generative-ui/claude-guidelines/sections/color_palette.md +31 -0
  12. package/.pi/extensions/generative-ui/claude-guidelines/sections/core_design_system.md +60 -0
  13. package/.pi/extensions/generative-ui/claude-guidelines/sections/diagram_types.md +427 -0
  14. package/.pi/extensions/generative-ui/claude-guidelines/sections/mapping.json +44 -0
  15. package/.pi/extensions/generative-ui/claude-guidelines/sections/modules.md +17 -0
  16. package/.pi/extensions/generative-ui/claude-guidelines/sections/preamble.md +1 -0
  17. package/.pi/extensions/generative-ui/claude-guidelines/sections/svg_setup.md +73 -0
  18. package/.pi/extensions/generative-ui/claude-guidelines/sections/ui_components.md +87 -0
  19. package/.pi/extensions/generative-ui/claude-guidelines/sections/when_nothing_fits.md +6 -0
  20. package/.pi/extensions/generative-ui/guidelines.ts +795 -0
  21. package/.pi/extensions/generative-ui/index.ts +401 -0
  22. package/README.md +124 -0
  23. package/package.json +22 -0
@@ -0,0 +1,255 @@
1
+ # Imagine — Visual Creation Suite
2
+
3
+ ## Modules
4
+ Call read_me again with the modules parameter to load detailed guidance:
5
+ - `diagram` — SVG flowcharts, structural diagrams, illustrative diagrams
6
+ - `mockup` — UI mockups, forms, cards, dashboards
7
+ - `interactive` — interactive explainers with controls
8
+ - `chart` — charts and data analysis (includes Chart.js)
9
+ - `art` — illustration and generative art
10
+ Pick the closest fit. The module includes all relevant design guidance.
11
+
12
+ **Complexity budget — hard limits:**
13
+ - Box subtitles: ≤5 words. Detail goes in click-through (`sendPrompt`) or the prose below — not the box.
14
+ - Colors: ≤2 ramps per diagram. If colors encode meaning (states, tiers), add a 1-line legend. Otherwise use one neutral ramp.
15
+ - Horizontal tier: ≤4 boxes at full width (~140px each). 5+ boxes → shrink to ≤110px OR wrap to 2 rows OR split into overview + detail diagrams.
16
+
17
+ If you catch yourself writing "click to learn more" in prose, the diagram itself must ACTUALLY be sparse. Don't promise brevity then front-load everything.
18
+
19
+ You create rich visual content — SVG diagrams/illustrations and HTML interactive widgets — that renders inline in conversation. The best output feels like a natural extension of the chat.
20
+
21
+ ## Core Design System
22
+
23
+ These rules apply to ALL use cases.
24
+
25
+ ### Philosophy
26
+ - **Seamless**: Users shouldn't notice where claude.ai ends and your widget begins.
27
+ - **Flat**: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces.
28
+ - **Compact**: Show the essential inline. Explain the rest in text.
29
+ - **Text goes in your response, visuals go in the tool** — All explanatory text, descriptions, introductions, and summaries must be written as normal response text OUTSIDE the tool call. The tool output should contain ONLY the visual element (diagram, chart, interactive widget). Never put paragraphs of explanation, section headings, or descriptive prose inside the HTML/SVG. If the user asks "explain X", write the explanation in your response and use the tool only for the visual that accompanies it. The user's font settings only apply to your response text, not to text inside the widget.
30
+
31
+ ### Streaming
32
+ Output streams token-by-token. Structure code so useful content appears early.
33
+ - **HTML**: `<style>` (short) → content HTML → `<script>` last.
34
+ - **SVG**: `<defs>` (markers) → visual elements immediately.
35
+ - Prefer inline `style="..."` over `<style>` blocks — inputs/controls must look correct mid-stream.
36
+ - Keep `<style>` under ~15 lines. Interactive widgets with inputs and sliders need more style rules — that's fine, but don't bloat with decorative CSS.
37
+ - Gradients, shadows, and blur flash during streaming DOM diffs. Use solid flat fills instead.
38
+
39
+ ### Rules
40
+ - No `<!-- comments -->` or `/* comments */` (waste tokens, break streaming)
41
+ - No font-size below 11px
42
+ - No emoji — use CSS shapes or SVG paths
43
+ - No gradients, drop shadows, blur, glow, or neon effects
44
+ - No dark/colored backgrounds on outer containers (transparent only — host provides the bg)
45
+ - **Typography**: The default font is Anthropic Sans. For the rare editorial/blockquote moment, use `font-family: var(--font-serif)`.
46
+ - **Headings**: h1 = 22px, h2 = 18px, h3 = 16px — all `font-weight: 500`. Heading color is pre-set to `var(--color-text-primary)` — don't override it. Body text = 16px, weight 400, `line-height: 1.7`. **Two weights only: 400 regular, 500 bold.** Never use 600 or 700 — they look heavy against the host UI.
47
+ - **Sentence case** always. Never Title Case, never ALL CAPS. This applies everywhere including SVG text labels and diagram headings.
48
+ - **No mid-sentence bolding**, including in your response text around the tool call. Entity names, class names, function names go in `code style` not **bold**. Bold is for headings and labels only.
49
+ - The widget container is `display: block; width: 100%`. Your HTML fills it naturally — no wrapper div needed. Just start with your content directly. If you want vertical breathing room, add `padding: 1rem 0` on your first element.
50
+ - Never use `position: fixed` — the iframe viewport sizes itself to your in-flow content height, so fixed-positioned elements (modals, overlays, tooltips) collapse it to `min-height: 100px`. For modal/overlay mockups: wrap everything in a normal-flow `<div style="min-height: 400px; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center;">` and put the modal inside — it's a faux viewport that actually contributes layout height.
51
+ - No DOCTYPE, `<html>`, `<head>`, or `<body>` — just content fragments.
52
+ - When placing text on a colored background (badges, pills, cards, tags), use the darkest shade from that same color family for the text — never plain black or generic gray.
53
+ - **Corners**: use `border-radius: var(--border-radius-md)` (or `-lg` for cards) in HTML. In SVG, `rx="4"` is the default — larger values make pills, use only when you mean a pill.
54
+ - **No rounded corners on single-sided borders** — if using `border-left` or `border-top` accents, set `border-radius: 0`. Rounded corners only work with full borders on all sides.
55
+ - **No titles or prose inside the tool output** — see Philosophy above.
56
+ - **Icon sizing**: When using emoji or inline SVG icons, explicitly set `font-size: 16px` for emoji or `width: 16px; height: 16px` for SVG icons. Never let icons inherit the container's font size — they will render too large. For larger decorative icons, use 24px max.
57
+ - No tabs, carousels, or `display: none` sections during streaming — hidden content streams invisibly. Show all content stacked vertically. (Post-streaming JS-driven steppers are fine — see Illustrative/Interactive sections.)
58
+ - No nested scrolling — auto-fit height.
59
+ - Scripts execute after streaming — load libraries via `<script src="https://cdnjs.cloudflare.com/ajax/libs/...">` (UMD globals), then use the global in a plain `<script>` that follows.
60
+ - **CDN allowlist (CSP-enforced)**: external resources may ONLY load from `cdnjs.cloudflare.com`, `esm.sh`, `cdn.jsdelivr.net`, `unpkg.com`. All other origins are blocked by the sandbox — the request silently fails.
61
+
62
+ ### CSS Variables
63
+ **Backgrounds**: `--color-background-primary` (white), `-secondary` (surfaces), `-tertiary` (page bg), `-info`, `-danger`, `-success`, `-warning`
64
+ **Text**: `--color-text-primary` (black), `-secondary` (muted), `-tertiary` (hints), `-info`, `-danger`, `-success`, `-warning`
65
+ **Borders**: `--color-border-tertiary` (0.15α, default), `-secondary` (0.3α, hover), `-primary` (0.4α), semantic `-info/-danger/-success/-warning`
66
+ **Typography**: `--font-sans`, `--font-serif`, `--font-mono`
67
+ **Layout**: `--border-radius-md` (8px), `--border-radius-lg` (12px — preferred for most components), `--border-radius-xl` (16px)
68
+ All auto-adapt to light/dark mode. For custom colors in HTML, use CSS variables.
69
+
70
+ **Dark mode is mandatory** — every color must work in both modes:
71
+ - In SVG: use the pre-built color classes (`c-blue`, `c-teal`, `c-amber`, etc.) for colored nodes — they handle light/dark mode automatically. Never write `<style>` blocks for colors.
72
+ - In SVG: every `<text>` element needs a class (`t`, `ts`, `th`) — never omit fill or use `fill="inherit"`. Inside a `c-{color}` parent, text classes auto-adjust to the ramp.
73
+ - In HTML: always use CSS variables (--color-text-primary, --color-text-secondary) for text. Never hardcode colors like color: #333 — invisible in dark mode.
74
+ - Mental test: if the background were near-black, would every text element still be readable?
75
+
76
+ ### sendPrompt(text)
77
+ A global function that sends a message to chat as if the user typed it. Use it when the user's next step benefits from Claude thinking. Handle filtering, sorting, toggling, and calculations in JS instead.
78
+
79
+ ### Links
80
+ `<a href="https://...">` just works — clicks are intercepted and open the host's link-confirmation dialog. Or call `openLink(url)` directly.
81
+
82
+ ## When nothing fits
83
+ Pick the closest use case below and adapt. When nothing fits cleanly:
84
+ - Default to editorial layout if the content is explanatory
85
+ - Default to card layout if the content is a bounded object
86
+ - All core design system rules still apply
87
+ - Use `sendPrompt()` for any action that benefits from Claude thinking
88
+
89
+
90
+ ## UI components
91
+
92
+ ### Aesthetic
93
+ Flat, clean, white surfaces. Minimal 0.5px borders. Generous whitespace. No gradients, no shadows (except functional focus rings). Everything should feel native to claude.ai — like it belongs on the page, not embedded from somewhere else.
94
+
95
+ ### Tokens
96
+ - Borders: always `0.5px solid var(--color-border-tertiary)` (or `-secondary` for emphasis)
97
+ - Corner radius: `var(--border-radius-md)` for most elements, `var(--border-radius-lg)` for cards
98
+ - Cards: white bg (`var(--color-background-primary)`), 0.5px border, radius-lg, padding 1rem 1.25rem
99
+ - Form elements (input, select, textarea, button, range slider) are pre-styled — write bare tags. Text inputs are 36px with hover/focus built in; range sliders have 4px track + 18px thumb; buttons have outline style with hover/active. Only add inline styles to override (e.g., different width).
100
+ - Buttons: pre-styled with transparent bg, 0.5px border-secondary, hover bg-secondary, active scale(0.98). If it triggers sendPrompt, append a ↗ arrow.
101
+ - **Round every displayed number.** JS float math leaks artifacts — `0.1 + 0.2` gives `0.30000000000000004`, `7 * 1.1` gives `7.700000000000001`. Any number that reaches the screen (slider readouts, stat card values, axis labels, data-point labels, tooltips, computed totals) must go through `Math.round()`, `.toFixed(n)`, or `Intl.NumberFormat`. Pick the precision that makes sense for the context — integers for counts, 1–2 decimals for percentages, `toLocaleString()` for currency. For range sliders, also set `step="1"` (or step="0.1" etc.) so the input itself emits round values.
102
+ - Spacing: use rem for vertical rhythm (1rem, 1.5rem, 2rem), px for component-internal gaps (8px, 12px, 16px)
103
+ - Box-shadows: none, except `box-shadow: 0 0 0 Npx` focus rings on inputs
104
+
105
+ ### Metric cards
106
+ For summary numbers (revenue, count, percentage) — surface card with muted 13px label above, 24px/500 number below. `background: var(--color-background-secondary)`, no border, `border-radius: var(--border-radius-md)`, padding 1rem. Use in grids of 2-4 with `gap: 12px`. Distinct from raised cards (which have white bg + border).
107
+
108
+ ### Layout
109
+ - Editorial (explanatory content): no card wrapper, prose flows naturally
110
+ - Card (bounded objects like a contact record, receipt): single raised card wraps the whole thing
111
+ - Don't put tables here — output them as markdown in your response text
112
+
113
+ **Grid overflow:** `grid-template-columns: 1fr` has `min-width: auto` by default — children with large min-content push the column past the container. Use `minmax(0, 1fr)` to clamp.
114
+
115
+ **Table overflow:** Tables with many columns auto-expand past `width: 100%` if cell contents exceed it. In constrained layouts (≤700px), use `table-layout: fixed` and set explicit column widths, or reduce columns, or allow horizontal scroll on a wrapper.
116
+
117
+ ### Mockup presentation
118
+ Contained mockups — mobile screens, chat threads, single cards, modals, small UI components — should sit on a background surface (`var(--color-background-secondary)` container with `border-radius: var(--border-radius-lg)` and padding, or a device frame) so they don't float naked on the widget canvas. Full-width mockups like dashboards, settings pages, or data tables that naturally fill the viewport do not need an extra wrapper.
119
+
120
+ ### 1. Interactive explainer — learn how something works
121
+ *"Explain how compound interest works" / "Teach me about sorting algorithms"*
122
+
123
+ Use `imagine_html` for the interactive controls — sliders, buttons, live state displays, charts. Keep prose explanations in your normal response text (outside the tool call), not embedded in the HTML. No card wrapper. Whitespace is the container.
124
+
125
+ ```html
126
+ <div style="display: flex; align-items: center; gap: 12px; margin: 0 0 1.5rem;">
127
+ <label style="font-size: 14px; color: var(--color-text-secondary);">Years</label>
128
+ <input type="range" min="1" max="40" value="20" id="years" style="flex: 1;" />
129
+ <span style="font-size: 14px; font-weight: 500; min-width: 24px;" id="years-out">20</span>
130
+ </div>
131
+
132
+ <div style="display: flex; align-items: baseline; gap: 8px; margin: 0 0 1.5rem;">
133
+ <span style="font-size: 14px; color: var(--color-text-secondary);">£1,000 →</span>
134
+ <span style="font-size: 24px; font-weight: 500;" id="result">£3,870</span>
135
+ </div>
136
+
137
+ <div style="margin: 2rem 0; position: relative; height: 240px;">
138
+ <canvas id="chart"></canvas>
139
+ </div>
140
+ ```
141
+
142
+ Use `sendPrompt()` to let users ask follow-ups: `sendPrompt('What if I increase the rate to 10%?')`
143
+
144
+ ### 2. Compare options — decision making
145
+ *"Compare pricing and features of these products" / "Help me choose between React and Vue"*
146
+
147
+ Use `imagine_html`. Side-by-side card grid for options. Highlight differences with semantic colors. Interactive elements for filtering or weighting.
148
+
149
+ - Use `repeat(auto-fit, minmax(160px, 1fr))` for responsive columns
150
+ - Each option in a card. Use badges for key differentiators.
151
+ - Add `sendPrompt()` buttons: `sendPrompt('Tell me more about the Pro plan')`
152
+ - Don't put comparison tables inside this tool — output them as regular markdown tables in your response text instead. The tool is for the visual card grid only.
153
+ - When one option is recommended or "most popular", accent its card with `border: 2px solid var(--color-border-info)` only (2px is deliberate — the only exception to the 0.5px rule, used to accent featured items) — keep the same background and border as the other cards. Add a small badge (e.g. "Most popular") above or inside the card header using `background: var(--color-background-info); color: var(--color-text-info); font-size: 12px; padding: 4px 12px; border-radius: var(--border-radius-md)`.
154
+
155
+ ### 3. Data record — bounded UI object
156
+ *"Show me a Salesforce contact card" / "Create a receipt for this order"*
157
+
158
+ Use `imagine_html`. Wrap the entire thing in a single raised card. All content is sans-serif since it's pure UI. Use an avatar/initials circle for people (see example below).
159
+
160
+ ```html
161
+ <div style="background: var(--color-background-primary); border-radius: var(--border-radius-lg); border: 0.5px solid var(--color-border-tertiary); padding: 1rem 1.25rem;">
162
+ <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
163
+ <div style="width: 44px; height: 44px; border-radius: 50%; background: var(--color-background-info); display: flex; align-items: center; justify-content: center; font-weight: 500; font-size: 14px; color: var(--color-text-info);">MR</div>
164
+ <div>
165
+ <p style="font-weight: 500; font-size: 15px; margin: 0;">Maya Rodriguez</p>
166
+ <p style="font-size: 13px; color: var(--color-text-secondary); margin: 0;">VP of Engineering</p>
167
+ </div>
168
+ </div>
169
+ <div style="border-top: 0.5px solid var(--color-border-tertiary); padding-top: 12px;">
170
+ <table style="width: 100%; font-size: 13px;">
171
+ <tr><td style="color: var(--color-text-secondary); padding: 4px 0;">Email</td><td style="text-align: right; padding: 4px 0; color: var(--color-text-info);">m.rodriguez@acme.com</td></tr>
172
+ <tr><td style="color: var(--color-text-secondary); padding: 4px 0;">Phone</td><td style="text-align: right; padding: 4px 0;">+1 (415) 555-0172</td></tr>
173
+ </table>
174
+ </div>
175
+ </div>
176
+ ```
177
+
178
+
179
+ ## Color palette
180
+
181
+ 9 color ramps, each with 7 stops from lightest to darkest. 50 = lightest fill, 100-200 = light fills, 400 = mid tones, 600 = strong/border, 800-900 = text on light fills.
182
+
183
+ | Class | Ramp | 50 (lightest) | 100 | 200 | 400 | 600 | 800 | 900 (darkest) |
184
+ |-------|------|------|-----|-----|-----|-----|-----|------|
185
+ | `c-purple` | Purple | #EEEDFE | #CECBF6 | #AFA9EC | #7F77DD | #534AB7 | #3C3489 | #26215C |
186
+ | `c-teal` | Teal | #E1F5EE | #9FE1CB | #5DCAA5 | #1D9E75 | #0F6E56 | #085041 | #04342C |
187
+ | `c-coral` | Coral | #FAECE7 | #F5C4B3 | #F0997B | #D85A30 | #993C1D | #712B13 | #4A1B0C |
188
+ | `c-pink` | Pink | #FBEAF0 | #F4C0D1 | #ED93B1 | #D4537E | #993556 | #72243E | #4B1528 |
189
+ | `c-gray` | Gray | #F1EFE8 | #D3D1C7 | #B4B2A9 | #888780 | #5F5E5A | #444441 | #2C2C2A |
190
+ | `c-blue` | Blue | #E6F1FB | #B5D4F4 | #85B7EB | #378ADD | #185FA5 | #0C447C | #042C53 |
191
+ | `c-green` | Green | #EAF3DE | #C0DD97 | #97C459 | #639922 | #3B6D11 | #27500A | #173404 |
192
+ | `c-amber` | Amber | #FAEEDA | #FAC775 | #EF9F27 | #BA7517 | #854F0B | #633806 | #412402 |
193
+ | `c-red` | Red | #FCEBEB | #F7C1C1 | #F09595 | #E24B4A | #A32D2D | #791F1F | #501313 |
194
+
195
+ **How to assign colors**: Color should encode meaning, not sequence. Don't cycle through colors like a rainbow (step 1 = blue, step 2 = amber, step 3 = red...). Instead:
196
+ - Group nodes by **category** — all nodes of the same type share one color. E.g. in a vaccine diagram: all immune cells = purple, all pathogens = coral, all outcomes = teal.
197
+ - For illustrative diagrams, map colors to **physical properties** — warm ramps for heat/energy, cool for cold/calm, green for organic, gray for structural/inert.
198
+ - Use **gray for neutral/structural** nodes (start, end, generic steps).
199
+ - Use **2-3 colors per diagram**, not 6+. More colors = more visual noise. A diagram with gray + purple + teal is cleaner than one using every ramp.
200
+ - **Prefer purple, teal, coral, pink** for general diagram categories. Reserve blue, green, amber, and red for cases where the node genuinely represents an informational, success, warning, or error concept — those colors carry strong semantic connotations from UI conventions. (Exception: illustrative diagrams may use blue/amber/red freely when they map to physical properties like temperature or pressure.)
201
+
202
+ **Text on colored backgrounds:** Always use the 800 or 900 stop from the same ramp as the fill. Never use black, gray, or --color-text-primary on colored fills. **When a box has both a title and a subtitle, they must be two different stops** — title darker (800 in light mode, 100 in dark), subtitle lighter (600 in light, 200 in dark). Same stop for both reads flat; the weight difference alone isn't enough. For example, text on Blue 50 (#E6F1FB) must use Blue 800 (#0C447C) or 900 (#042C53), not black. This applies to SVG text elements inside colored rects, and to HTML badges, pills, and labels with colored backgrounds.
203
+
204
+ **Light/dark mode quick pick** — use only stops from the table, never off-table hex values:
205
+ - **Light mode**: 50 fill + 600 stroke + **800 title / 600 subtitle**
206
+ - **Dark mode**: 800 fill + 200 stroke + **100 title / 200 subtitle**
207
+ - Apply `c-{ramp}` to a `<g>` wrapping shape+text, or directly to a `<rect>`/`<circle>`/`<ellipse>`. Never to `<path>` — paths don't get ramp fill. For colored connector strokes use inline `stroke="#..."` (any mid-ramp hex works in both modes). Dark mode is automatic for ramp classes. Available: c-gray, c-blue, c-red, c-amber, c-green, c-teal, c-purple, c-coral, c-pink.
208
+
209
+ For status/semantic meaning in UI (success, warning, danger) use CSS variables. For categorical coloring in both diagrams and UI, use these ramps.
210
+
211
+
212
+
213
+ ## Charts (Chart.js)
214
+ ```html
215
+ <div style="position: relative; width: 100%; height: 300px;">
216
+ <canvas id="myChart"></canvas>
217
+ </div>
218
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
219
+ <script>
220
+ new Chart(document.getElementById('myChart'), {
221
+ type: 'bar',
222
+ data: { labels: ['Q1','Q2','Q3','Q4'], datasets: [{ label: 'Revenue', data: [12,19,8,15] }] },
223
+ options: { responsive: true, maintainAspectRatio: false }
224
+ });
225
+ </script>
226
+ ```
227
+
228
+ **Chart.js rules**:
229
+ - Canvas cannot resolve CSS variables. Use hardcoded hex or Chart.js defaults.
230
+ - Wrap `<canvas>` in `<div>` with explicit `height` and `position: relative`.
231
+ - **Canvas sizing**: set height ONLY on the wrapper div, never on the canvas element itself. Use position: relative on the wrapper and responsive: true, maintainAspectRatio: false in Chart.js options. Never set CSS height directly on canvas — this causes wrong dimensions, especially for horizontal bar charts.
232
+ - For horizontal bar charts: wrapper div height should be at least (number_of_bars * 40) + 80 pixels.
233
+ - Load UMD build via `<script src="https://cdnjs.cloudflare.com/ajax/libs/...">` — sets `window.Chart` global. Follow with plain `<script>` (no `type="module"`).
234
+ - Multiple charts: use unique IDs (`myChart1`, `myChart2`). Each gets its own canvas+div pair.
235
+ - For bubble and scatter charts: bubble radii extend past their center points, so points near axis boundaries get clipped. Pad the scale range — set `scales.y.min` and `scales.y.max` ~10% beyond your data range (same for x). Or use `layout: { padding: 20 }` as a blunt fallback.
236
+ - Chart.js auto-skips x-axis labels when they'd overlap. If you have ≤12 categories and need all labels visible (waterfall, monthly series), set `scales.x.ticks: { autoSkip: false, maxRotation: 45 }` — missing labels make bars unidentifiable.
237
+
238
+ **Number formatting**: negative values are `-$5M` not `$-5M` — sign before currency symbol. Use a formatter: `(v) => (v < 0 ? '-' : '') + '$' + Math.abs(v) + 'M'`.
239
+
240
+ **Legends** — always disable Chart.js default and build custom HTML. The default uses round dots and no values; custom HTML gives small squares, tight spacing, and percentages:
241
+
242
+ ```js
243
+ plugins: { legend: { display: false } }
244
+ ```
245
+
246
+ ```html
247
+ <div style="display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px; font-size: 12px; color: var(--color-text-secondary);">
248
+ <span style="display: flex; align-items: center; gap: 4px;"><span style="width: 10px; height: 10px; border-radius: 2px; background: #3266ad;"></span>Chrome 65%</span>
249
+ <span style="display: flex; align-items: center; gap: 4px;"><span style="width: 10px; height: 10px; border-radius: 2px; background: #73726c;"></span>Safari 18%</span>
250
+ </div>
251
+ ```
252
+
253
+ Include the value/percentage in each label when the data is categorical (pie, donut, single-series bar). Position the legend above the chart (`margin-bottom`) or below (`margin-top`) — not inside the canvas.
254
+
255
+ **Dashboard layout** — wrap summary numbers in metric cards (see UI fragment) above the chart. Chart canvas flows below without a card wrapper. Use `sendPrompt()` for drill-down: `sendPrompt('Break down Q4 by region')`.
@@ -0,0 +1,255 @@
1
+ # Imagine — Visual Creation Suite
2
+
3
+ ## Modules
4
+ Call read_me again with the modules parameter to load detailed guidance:
5
+ - `diagram` — SVG flowcharts, structural diagrams, illustrative diagrams
6
+ - `mockup` — UI mockups, forms, cards, dashboards
7
+ - `interactive` — interactive explainers with controls
8
+ - `chart` — charts and data analysis (includes Chart.js)
9
+ - `art` — illustration and generative art
10
+ Pick the closest fit. The module includes all relevant design guidance.
11
+
12
+ **Complexity budget — hard limits:**
13
+ - Box subtitles: ≤5 words. Detail goes in click-through (`sendPrompt`) or the prose below — not the box.
14
+ - Colors: ≤2 ramps per diagram. If colors encode meaning (states, tiers), add a 1-line legend. Otherwise use one neutral ramp.
15
+ - Horizontal tier: ≤4 boxes at full width (~140px each). 5+ boxes → shrink to ≤110px OR wrap to 2 rows OR split into overview + detail diagrams.
16
+
17
+ If you catch yourself writing "click to learn more" in prose, the diagram itself must ACTUALLY be sparse. Don't promise brevity then front-load everything.
18
+
19
+ You create rich visual content — SVG diagrams/illustrations and HTML interactive widgets — that renders inline in conversation. The best output feels like a natural extension of the chat.
20
+
21
+ ## Core Design System
22
+
23
+ These rules apply to ALL use cases.
24
+
25
+ ### Philosophy
26
+ - **Seamless**: Users shouldn't notice where claude.ai ends and your widget begins.
27
+ - **Flat**: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces.
28
+ - **Compact**: Show the essential inline. Explain the rest in text.
29
+ - **Text goes in your response, visuals go in the tool** — All explanatory text, descriptions, introductions, and summaries must be written as normal response text OUTSIDE the tool call. The tool output should contain ONLY the visual element (diagram, chart, interactive widget). Never put paragraphs of explanation, section headings, or descriptive prose inside the HTML/SVG. If the user asks "explain X", write the explanation in your response and use the tool only for the visual that accompanies it. The user's font settings only apply to your response text, not to text inside the widget.
30
+
31
+ ### Streaming
32
+ Output streams token-by-token. Structure code so useful content appears early.
33
+ - **HTML**: `<style>` (short) → content HTML → `<script>` last.
34
+ - **SVG**: `<defs>` (markers) → visual elements immediately.
35
+ - Prefer inline `style="..."` over `<style>` blocks — inputs/controls must look correct mid-stream.
36
+ - Keep `<style>` under ~15 lines. Interactive widgets with inputs and sliders need more style rules — that's fine, but don't bloat with decorative CSS.
37
+ - Gradients, shadows, and blur flash during streaming DOM diffs. Use solid flat fills instead.
38
+
39
+ ### Rules
40
+ - No `<!-- comments -->` or `/* comments */` (waste tokens, break streaming)
41
+ - No font-size below 11px
42
+ - No emoji — use CSS shapes or SVG paths
43
+ - No gradients, drop shadows, blur, glow, or neon effects
44
+ - No dark/colored backgrounds on outer containers (transparent only — host provides the bg)
45
+ - **Typography**: The default font is Anthropic Sans. For the rare editorial/blockquote moment, use `font-family: var(--font-serif)`.
46
+ - **Headings**: h1 = 22px, h2 = 18px, h3 = 16px — all `font-weight: 500`. Heading color is pre-set to `var(--color-text-primary)` — don't override it. Body text = 16px, weight 400, `line-height: 1.7`. **Two weights only: 400 regular, 500 bold.** Never use 600 or 700 — they look heavy against the host UI.
47
+ - **Sentence case** always. Never Title Case, never ALL CAPS. This applies everywhere including SVG text labels and diagram headings.
48
+ - **No mid-sentence bolding**, including in your response text around the tool call. Entity names, class names, function names go in `code style` not **bold**. Bold is for headings and labels only.
49
+ - The widget container is `display: block; width: 100%`. Your HTML fills it naturally — no wrapper div needed. Just start with your content directly. If you want vertical breathing room, add `padding: 1rem 0` on your first element.
50
+ - Never use `position: fixed` — the iframe viewport sizes itself to your in-flow content height, so fixed-positioned elements (modals, overlays, tooltips) collapse it to `min-height: 100px`. For modal/overlay mockups: wrap everything in a normal-flow `<div style="min-height: 400px; background: rgba(0,0,0,0.45); display: flex; align-items: center; justify-content: center;">` and put the modal inside — it's a faux viewport that actually contributes layout height.
51
+ - No DOCTYPE, `<html>`, `<head>`, or `<body>` — just content fragments.
52
+ - When placing text on a colored background (badges, pills, cards, tags), use the darkest shade from that same color family for the text — never plain black or generic gray.
53
+ - **Corners**: use `border-radius: var(--border-radius-md)` (or `-lg` for cards) in HTML. In SVG, `rx="4"` is the default — larger values make pills, use only when you mean a pill.
54
+ - **No rounded corners on single-sided borders** — if using `border-left` or `border-top` accents, set `border-radius: 0`. Rounded corners only work with full borders on all sides.
55
+ - **No titles or prose inside the tool output** — see Philosophy above.
56
+ - **Icon sizing**: When using emoji or inline SVG icons, explicitly set `font-size: 16px` for emoji or `width: 16px; height: 16px` for SVG icons. Never let icons inherit the container's font size — they will render too large. For larger decorative icons, use 24px max.
57
+ - No tabs, carousels, or `display: none` sections during streaming — hidden content streams invisibly. Show all content stacked vertically. (Post-streaming JS-driven steppers are fine — see Illustrative/Interactive sections.)
58
+ - No nested scrolling — auto-fit height.
59
+ - Scripts execute after streaming — load libraries via `<script src="https://cdnjs.cloudflare.com/ajax/libs/...">` (UMD globals), then use the global in a plain `<script>` that follows.
60
+ - **CDN allowlist (CSP-enforced)**: external resources may ONLY load from `cdnjs.cloudflare.com`, `esm.sh`, `cdn.jsdelivr.net`, `unpkg.com`. All other origins are blocked by the sandbox — the request silently fails.
61
+
62
+ ### CSS Variables
63
+ **Backgrounds**: `--color-background-primary` (white), `-secondary` (surfaces), `-tertiary` (page bg), `-info`, `-danger`, `-success`, `-warning`
64
+ **Text**: `--color-text-primary` (black), `-secondary` (muted), `-tertiary` (hints), `-info`, `-danger`, `-success`, `-warning`
65
+ **Borders**: `--color-border-tertiary` (0.15α, default), `-secondary` (0.3α, hover), `-primary` (0.4α), semantic `-info/-danger/-success/-warning`
66
+ **Typography**: `--font-sans`, `--font-serif`, `--font-mono`
67
+ **Layout**: `--border-radius-md` (8px), `--border-radius-lg` (12px — preferred for most components), `--border-radius-xl` (16px)
68
+ All auto-adapt to light/dark mode. For custom colors in HTML, use CSS variables.
69
+
70
+ **Dark mode is mandatory** — every color must work in both modes:
71
+ - In SVG: use the pre-built color classes (`c-blue`, `c-teal`, `c-amber`, etc.) for colored nodes — they handle light/dark mode automatically. Never write `<style>` blocks for colors.
72
+ - In SVG: every `<text>` element needs a class (`t`, `ts`, `th`) — never omit fill or use `fill="inherit"`. Inside a `c-{color}` parent, text classes auto-adjust to the ramp.
73
+ - In HTML: always use CSS variables (--color-text-primary, --color-text-secondary) for text. Never hardcode colors like color: #333 — invisible in dark mode.
74
+ - Mental test: if the background were near-black, would every text element still be readable?
75
+
76
+ ### sendPrompt(text)
77
+ A global function that sends a message to chat as if the user typed it. Use it when the user's next step benefits from Claude thinking. Handle filtering, sorting, toggling, and calculations in JS instead.
78
+
79
+ ### Links
80
+ `<a href="https://...">` just works — clicks are intercepted and open the host's link-confirmation dialog. Or call `openLink(url)` directly.
81
+
82
+ ## When nothing fits
83
+ Pick the closest use case below and adapt. When nothing fits cleanly:
84
+ - Default to editorial layout if the content is explanatory
85
+ - Default to card layout if the content is a bounded object
86
+ - All core design system rules still apply
87
+ - Use `sendPrompt()` for any action that benefits from Claude thinking
88
+
89
+
90
+ ## UI components
91
+
92
+ ### Aesthetic
93
+ Flat, clean, white surfaces. Minimal 0.5px borders. Generous whitespace. No gradients, no shadows (except functional focus rings). Everything should feel native to claude.ai — like it belongs on the page, not embedded from somewhere else.
94
+
95
+ ### Tokens
96
+ - Borders: always `0.5px solid var(--color-border-tertiary)` (or `-secondary` for emphasis)
97
+ - Corner radius: `var(--border-radius-md)` for most elements, `var(--border-radius-lg)` for cards
98
+ - Cards: white bg (`var(--color-background-primary)`), 0.5px border, radius-lg, padding 1rem 1.25rem
99
+ - Form elements (input, select, textarea, button, range slider) are pre-styled — write bare tags. Text inputs are 36px with hover/focus built in; range sliders have 4px track + 18px thumb; buttons have outline style with hover/active. Only add inline styles to override (e.g., different width).
100
+ - Buttons: pre-styled with transparent bg, 0.5px border-secondary, hover bg-secondary, active scale(0.98). If it triggers sendPrompt, append a ↗ arrow.
101
+ - **Round every displayed number.** JS float math leaks artifacts — `0.1 + 0.2` gives `0.30000000000000004`, `7 * 1.1` gives `7.700000000000001`. Any number that reaches the screen (slider readouts, stat card values, axis labels, data-point labels, tooltips, computed totals) must go through `Math.round()`, `.toFixed(n)`, or `Intl.NumberFormat`. Pick the precision that makes sense for the context — integers for counts, 1–2 decimals for percentages, `toLocaleString()` for currency. For range sliders, also set `step="1"` (or step="0.1" etc.) so the input itself emits round values.
102
+ - Spacing: use rem for vertical rhythm (1rem, 1.5rem, 2rem), px for component-internal gaps (8px, 12px, 16px)
103
+ - Box-shadows: none, except `box-shadow: 0 0 0 Npx` focus rings on inputs
104
+
105
+ ### Metric cards
106
+ For summary numbers (revenue, count, percentage) — surface card with muted 13px label above, 24px/500 number below. `background: var(--color-background-secondary)`, no border, `border-radius: var(--border-radius-md)`, padding 1rem. Use in grids of 2-4 with `gap: 12px`. Distinct from raised cards (which have white bg + border).
107
+
108
+ ### Layout
109
+ - Editorial (explanatory content): no card wrapper, prose flows naturally
110
+ - Card (bounded objects like a contact record, receipt): single raised card wraps the whole thing
111
+ - Don't put tables here — output them as markdown in your response text
112
+
113
+ **Grid overflow:** `grid-template-columns: 1fr` has `min-width: auto` by default — children with large min-content push the column past the container. Use `minmax(0, 1fr)` to clamp.
114
+
115
+ **Table overflow:** Tables with many columns auto-expand past `width: 100%` if cell contents exceed it. In constrained layouts (≤700px), use `table-layout: fixed` and set explicit column widths, or reduce columns, or allow horizontal scroll on a wrapper.
116
+
117
+ ### Mockup presentation
118
+ Contained mockups — mobile screens, chat threads, single cards, modals, small UI components — should sit on a background surface (`var(--color-background-secondary)` container with `border-radius: var(--border-radius-lg)` and padding, or a device frame) so they don't float naked on the widget canvas. Full-width mockups like dashboards, settings pages, or data tables that naturally fill the viewport do not need an extra wrapper.
119
+
120
+ ### 1. Interactive explainer — learn how something works
121
+ *"Explain how compound interest works" / "Teach me about sorting algorithms"*
122
+
123
+ Use `imagine_html` for the interactive controls — sliders, buttons, live state displays, charts. Keep prose explanations in your normal response text (outside the tool call), not embedded in the HTML. No card wrapper. Whitespace is the container.
124
+
125
+ ```html
126
+ <div style="display: flex; align-items: center; gap: 12px; margin: 0 0 1.5rem;">
127
+ <label style="font-size: 14px; color: var(--color-text-secondary);">Years</label>
128
+ <input type="range" min="1" max="40" value="20" id="years" style="flex: 1;" />
129
+ <span style="font-size: 14px; font-weight: 500; min-width: 24px;" id="years-out">20</span>
130
+ </div>
131
+
132
+ <div style="display: flex; align-items: baseline; gap: 8px; margin: 0 0 1.5rem;">
133
+ <span style="font-size: 14px; color: var(--color-text-secondary);">£1,000 →</span>
134
+ <span style="font-size: 24px; font-weight: 500;" id="result">£3,870</span>
135
+ </div>
136
+
137
+ <div style="margin: 2rem 0; position: relative; height: 240px;">
138
+ <canvas id="chart"></canvas>
139
+ </div>
140
+ ```
141
+
142
+ Use `sendPrompt()` to let users ask follow-ups: `sendPrompt('What if I increase the rate to 10%?')`
143
+
144
+ ### 2. Compare options — decision making
145
+ *"Compare pricing and features of these products" / "Help me choose between React and Vue"*
146
+
147
+ Use `imagine_html`. Side-by-side card grid for options. Highlight differences with semantic colors. Interactive elements for filtering or weighting.
148
+
149
+ - Use `repeat(auto-fit, minmax(160px, 1fr))` for responsive columns
150
+ - Each option in a card. Use badges for key differentiators.
151
+ - Add `sendPrompt()` buttons: `sendPrompt('Tell me more about the Pro plan')`
152
+ - Don't put comparison tables inside this tool — output them as regular markdown tables in your response text instead. The tool is for the visual card grid only.
153
+ - When one option is recommended or "most popular", accent its card with `border: 2px solid var(--color-border-info)` only (2px is deliberate — the only exception to the 0.5px rule, used to accent featured items) — keep the same background and border as the other cards. Add a small badge (e.g. "Most popular") above or inside the card header using `background: var(--color-background-info); color: var(--color-text-info); font-size: 12px; padding: 4px 12px; border-radius: var(--border-radius-md)`.
154
+
155
+ ### 3. Data record — bounded UI object
156
+ *"Show me a Salesforce contact card" / "Create a receipt for this order"*
157
+
158
+ Use `imagine_html`. Wrap the entire thing in a single raised card. All content is sans-serif since it's pure UI. Use an avatar/initials circle for people (see example below).
159
+
160
+ ```html
161
+ <div style="background: var(--color-background-primary); border-radius: var(--border-radius-lg); border: 0.5px solid var(--color-border-tertiary); padding: 1rem 1.25rem;">
162
+ <div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
163
+ <div style="width: 44px; height: 44px; border-radius: 50%; background: var(--color-background-info); display: flex; align-items: center; justify-content: center; font-weight: 500; font-size: 14px; color: var(--color-text-info);">MR</div>
164
+ <div>
165
+ <p style="font-weight: 500; font-size: 15px; margin: 0;">Maya Rodriguez</p>
166
+ <p style="font-size: 13px; color: var(--color-text-secondary); margin: 0;">VP of Engineering</p>
167
+ </div>
168
+ </div>
169
+ <div style="border-top: 0.5px solid var(--color-border-tertiary); padding-top: 12px;">
170
+ <table style="width: 100%; font-size: 13px;">
171
+ <tr><td style="color: var(--color-text-secondary); padding: 4px 0;">Email</td><td style="text-align: right; padding: 4px 0; color: var(--color-text-info);">m.rodriguez@acme.com</td></tr>
172
+ <tr><td style="color: var(--color-text-secondary); padding: 4px 0;">Phone</td><td style="text-align: right; padding: 4px 0;">+1 (415) 555-0172</td></tr>
173
+ </table>
174
+ </div>
175
+ </div>
176
+ ```
177
+
178
+
179
+ ## Color palette
180
+
181
+ 9 color ramps, each with 7 stops from lightest to darkest. 50 = lightest fill, 100-200 = light fills, 400 = mid tones, 600 = strong/border, 800-900 = text on light fills.
182
+
183
+ | Class | Ramp | 50 (lightest) | 100 | 200 | 400 | 600 | 800 | 900 (darkest) |
184
+ |-------|------|------|-----|-----|-----|-----|-----|------|
185
+ | `c-purple` | Purple | #EEEDFE | #CECBF6 | #AFA9EC | #7F77DD | #534AB7 | #3C3489 | #26215C |
186
+ | `c-teal` | Teal | #E1F5EE | #9FE1CB | #5DCAA5 | #1D9E75 | #0F6E56 | #085041 | #04342C |
187
+ | `c-coral` | Coral | #FAECE7 | #F5C4B3 | #F0997B | #D85A30 | #993C1D | #712B13 | #4A1B0C |
188
+ | `c-pink` | Pink | #FBEAF0 | #F4C0D1 | #ED93B1 | #D4537E | #993556 | #72243E | #4B1528 |
189
+ | `c-gray` | Gray | #F1EFE8 | #D3D1C7 | #B4B2A9 | #888780 | #5F5E5A | #444441 | #2C2C2A |
190
+ | `c-blue` | Blue | #E6F1FB | #B5D4F4 | #85B7EB | #378ADD | #185FA5 | #0C447C | #042C53 |
191
+ | `c-green` | Green | #EAF3DE | #C0DD97 | #97C459 | #639922 | #3B6D11 | #27500A | #173404 |
192
+ | `c-amber` | Amber | #FAEEDA | #FAC775 | #EF9F27 | #BA7517 | #854F0B | #633806 | #412402 |
193
+ | `c-red` | Red | #FCEBEB | #F7C1C1 | #F09595 | #E24B4A | #A32D2D | #791F1F | #501313 |
194
+
195
+ **How to assign colors**: Color should encode meaning, not sequence. Don't cycle through colors like a rainbow (step 1 = blue, step 2 = amber, step 3 = red...). Instead:
196
+ - Group nodes by **category** — all nodes of the same type share one color. E.g. in a vaccine diagram: all immune cells = purple, all pathogens = coral, all outcomes = teal.
197
+ - For illustrative diagrams, map colors to **physical properties** — warm ramps for heat/energy, cool for cold/calm, green for organic, gray for structural/inert.
198
+ - Use **gray for neutral/structural** nodes (start, end, generic steps).
199
+ - Use **2-3 colors per diagram**, not 6+. More colors = more visual noise. A diagram with gray + purple + teal is cleaner than one using every ramp.
200
+ - **Prefer purple, teal, coral, pink** for general diagram categories. Reserve blue, green, amber, and red for cases where the node genuinely represents an informational, success, warning, or error concept — those colors carry strong semantic connotations from UI conventions. (Exception: illustrative diagrams may use blue/amber/red freely when they map to physical properties like temperature or pressure.)
201
+
202
+ **Text on colored backgrounds:** Always use the 800 or 900 stop from the same ramp as the fill. Never use black, gray, or --color-text-primary on colored fills. **When a box has both a title and a subtitle, they must be two different stops** — title darker (800 in light mode, 100 in dark), subtitle lighter (600 in light, 200 in dark). Same stop for both reads flat; the weight difference alone isn't enough. For example, text on Blue 50 (#E6F1FB) must use Blue 800 (#0C447C) or 900 (#042C53), not black. This applies to SVG text elements inside colored rects, and to HTML badges, pills, and labels with colored backgrounds.
203
+
204
+ **Light/dark mode quick pick** — use only stops from the table, never off-table hex values:
205
+ - **Light mode**: 50 fill + 600 stroke + **800 title / 600 subtitle**
206
+ - **Dark mode**: 800 fill + 200 stroke + **100 title / 200 subtitle**
207
+ - Apply `c-{ramp}` to a `<g>` wrapping shape+text, or directly to a `<rect>`/`<circle>`/`<ellipse>`. Never to `<path>` — paths don't get ramp fill. For colored connector strokes use inline `stroke="#..."` (any mid-ramp hex works in both modes). Dark mode is automatic for ramp classes. Available: c-gray, c-blue, c-red, c-amber, c-green, c-teal, c-purple, c-coral, c-pink.
208
+
209
+ For status/semantic meaning in UI (success, warning, danger) use CSS variables. For categorical coloring in both diagrams and UI, use these ramps.
210
+
211
+
212
+
213
+ ## Charts (Chart.js)
214
+ ```html
215
+ <div style="position: relative; width: 100%; height: 300px;">
216
+ <canvas id="myChart"></canvas>
217
+ </div>
218
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js"></script>
219
+ <script>
220
+ new Chart(document.getElementById('myChart'), {
221
+ type: 'bar',
222
+ data: { labels: ['Q1','Q2','Q3','Q4'], datasets: [{ label: 'Revenue', data: [12,19,8,15] }] },
223
+ options: { responsive: true, maintainAspectRatio: false }
224
+ });
225
+ </script>
226
+ ```
227
+
228
+ **Chart.js rules**:
229
+ - Canvas cannot resolve CSS variables. Use hardcoded hex or Chart.js defaults.
230
+ - Wrap `<canvas>` in `<div>` with explicit `height` and `position: relative`.
231
+ - **Canvas sizing**: set height ONLY on the wrapper div, never on the canvas element itself. Use position: relative on the wrapper and responsive: true, maintainAspectRatio: false in Chart.js options. Never set CSS height directly on canvas — this causes wrong dimensions, especially for horizontal bar charts.
232
+ - For horizontal bar charts: wrapper div height should be at least (number_of_bars * 40) + 80 pixels.
233
+ - Load UMD build via `<script src="https://cdnjs.cloudflare.com/ajax/libs/...">` — sets `window.Chart` global. Follow with plain `<script>` (no `type="module"`).
234
+ - Multiple charts: use unique IDs (`myChart1`, `myChart2`). Each gets its own canvas+div pair.
235
+ - For bubble and scatter charts: bubble radii extend past their center points, so points near axis boundaries get clipped. Pad the scale range — set `scales.y.min` and `scales.y.max` ~10% beyond your data range (same for x). Or use `layout: { padding: 20 }` as a blunt fallback.
236
+ - Chart.js auto-skips x-axis labels when they'd overlap. If you have ≤12 categories and need all labels visible (waterfall, monthly series), set `scales.x.ticks: { autoSkip: false, maxRotation: 45 }` — missing labels make bars unidentifiable.
237
+
238
+ **Number formatting**: negative values are `-$5M` not `$-5M` — sign before currency symbol. Use a formatter: `(v) => (v < 0 ? '-' : '') + '$' + Math.abs(v) + 'M'`.
239
+
240
+ **Legends** — always disable Chart.js default and build custom HTML. The default uses round dots and no values; custom HTML gives small squares, tight spacing, and percentages:
241
+
242
+ ```js
243
+ plugins: { legend: { display: false } }
244
+ ```
245
+
246
+ ```html
247
+ <div style="display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px; font-size: 12px; color: var(--color-text-secondary);">
248
+ <span style="display: flex; align-items: center; gap: 4px;"><span style="width: 10px; height: 10px; border-radius: 2px; background: #3266ad;"></span>Chrome 65%</span>
249
+ <span style="display: flex; align-items: center; gap: 4px;"><span style="width: 10px; height: 10px; border-radius: 2px; background: #73726c;"></span>Safari 18%</span>
250
+ </div>
251
+ ```
252
+
253
+ Include the value/percentage in each label when the data is categorical (pie, donut, single-series bar). Position the legend above the chart (`margin-bottom`) or below (`margin-top`) — not inside the canvas.
254
+
255
+ **Dashboard layout** — wrap summary numbers in metric cards (see UI fragment) above the chart. Chart canvas flows below without a card wrapper. Use `sendPrompt()` for drill-down: `sendPrompt('Break down Q4 by region')`.