Generative UI Skill
This skill contains the complete design system for Claude's built-in show_widget tool — the generative UI feature that renders interactive HTML/SVG widgets inline in claude.ai conversations. The guidelines below are the actual Anthropic "Imagine — Visual Creation Suite" design rules, extracted so you can produce high-quality widgets directly without needing the read_me setup call.
How it works: On claude.ai, Claude has access to the show_widget tool which renders raw HTML/SVG fragments inline in the conversation. This skill provides the design system, templates, and patterns to use it well.
Step 1: Pick the Right Visual Type
Route on the verb, not the noun. Same subject, different visual depending on what was asked:
| User says |
Type |
Format |
| "how does X work" |
Illustrative diagram |
SVG |
| "X architecture" |
Structural diagram |
SVG |
| "what are the steps" |
Flowchart |
SVG |
| "explain compound interest" |
Interactive explainer |
HTML |
| "compare these options" |
Comparison grid |
HTML |
| "show revenue chart" |
Chart.js chart |
HTML |
| "create a contact card" |
Data record |
HTML |
| "draw a sunset" |
Art/illustration |
SVG |
Step 2: Build the Widget
Structure (strict order)
<style> → HTML content → <script>
Output streams token-by-token. Styles must exist before the elements they target, and scripts must run after the DOM is ready.
Philosophy
- Seamless: Users shouldn't notice where the host UI ends and your widget begins
- Flat: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces
- Compact: Show the essential inline. Explain the rest in text
- Text goes in your response, visuals go in the tool — all explanatory text, descriptions, and summaries must be written as normal response text OUTSIDE the tool call. The tool output should contain ONLY the visual element
Core Rules
- No
<!-- comments --> or /* comments */ (waste tokens, break streaming)
- No font-size below 11px
- No emoji — use CSS shapes or SVG paths
- No gradients, drop shadows, blur, glow, or neon effects
- No dark/colored backgrounds on outer containers (transparent only — host provides the bg)
- Typography: two weights only: 400 regular, 500 medium. Never use 600 or 700. Headings: h1=22px, h2=18px, h3=16px — all font-weight 500. Body text=16px, weight 400, line-height 1.7
- Sentence case always. Never Title Case, never ALL CAPS
- No mid-sentence bolding — entity names go in
code style not bold
- No
<!DOCTYPE>, <html>, <head>, or <body> — just content fragments
- No
position: fixed — use normal-flow layouts
- No tabs, carousels, or
display: none sections during streaming
- No nested scrolling — auto-fit height
- Corners:
border-radius: var(--border-radius-lg) for cards, var(--border-radius-md) for elements
- No rounded corners on single-sided borders (border-left, border-top)
- Round every displayed number — use
Math.round(), .toFixed(n), or Intl.NumberFormat
CDN Allowlist (CSP-enforced)
External resources may ONLY load from:
cdnjs.cloudflare.com
cdn.jsdelivr.net
unpkg.com
esm.sh
All other origins are blocked — the request silently fails.
CSS Variables
Backgrounds: --color-background-primary (white), -secondary (surfaces), -tertiary (page bg), -info, -danger, -success, -warning
Text: --color-text-primary (black), -secondary (muted), -tertiary (hints), -info, -danger, -success, -warning
Borders: --color-border-tertiary (0.15α, default), -secondary (0.3α, hover), -primary (0.4α), semantic -info/-danger/-success/-warning
Typography: --font-sans, --font-serif, --font-mono
Layout: --border-radius-md (8px), --border-radius-lg (12px), --border-radius-xl (16px)
All auto-adapt to light/dark mode.
Dark mode is mandatory — every color must work in both modes:
- In HTML: always use CSS variables for text. Never hardcode colors like
color: #333
- In SVG: use pre-built color classes (
c-blue, c-teal, etc.) — they handle light/dark automatically
- Mental test: if the background were near-black, would every text element still be readable?
sendPrompt(text)
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.
Step 3: Render with show_widget
The show_widget tool is built into claude.ai — no activation needed. Pass your widget code directly:
{
"title": "snake_case_widget_name",
"widget_code": "<style>...</style>\n<div>...</div>\n<script>...</script>"
}
| Parameter |
Type |
Required |
Description |
title |
string |
Yes |
Snake_case identifier for the widget |
widget_code |
string |
Yes |
HTML or SVG code. For SVG: start with <svg>. For HTML: content fragment |
For SVG output: start widget_code with <svg — it will be auto-detected and wrapped appropriately.
Step 4: Chart.js Template
For charts, use onload callback pattern to handle script load ordering:
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px;">
<div style="background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 1rem;">
<div style="font-size: 13px; color: var(--color-text-secondary);">Label</div>
<div style="font-size: 24px; font-weight: 500;" id="stat1">—</div>
</div>
</div>
<div style="position: relative; width: 100%; height: 300px; margin-top: 1rem;">
<canvas id="myChart"></canvas>
</div>
<div style="display: flex; align-items: center; gap: 12px; margin-top: 1rem;">
<label style="font-size: 14px; color: var(--color-text-secondary);">Parameter</label>
<input type="range" min="0" max="100" value="50" id="param" step="1" style="flex: 1;" />
<span style="font-size: 14px; font-weight: 500; min-width: 32px;" id="param-out">50</span>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.js"
<script>
function initChart() {
const slider = document.getElementById('param');
const out = document.getElementById('param-out');
let chart = null;
function update() {
const val = parseFloat(slider.value);
out.textContent = val;
document.getElementById('stat1').textContent = val.toFixed(1);
const labels = [], data = [];
for (let x = 0; x <= 100; x++) {
labels.push(x);
data.push(x * val / 100);
}
if (chart) chart.destroy();
chart = new Chart(document.getElementById('myChart'), {
type: 'line',
data: { labels, datasets: [{ data, borderColor: '#7F77DD', borderWidth: 2, pointRadius: 0, fill: false }] },
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false } },
scales: { x: { grid: { display: false } } }
}
});
}
slider.addEventListener('input', update);
update();
}
if (window.Chart) initChart();
</script>
Chart.js rules:
- Canvas cannot resolve CSS variables — use hardcoded hex
- Set height ONLY on the wrapper div, never on canvas itself
- Always
responsive: true, maintainAspectRatio: false
- Always disable default legend, build custom HTML legends
- Number formatting:
-$5M not $-5M (negative sign before currency symbol)
- Use
onload="initChart()" on CDN script tag + if (window.Chart) initChart(); as fallback
Step 5: SVG Diagram Template
For flowcharts and diagrams, use SVG with pre-built classes:
<svg width="100%" viewBox="0 0 680 H">
<defs>
<marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</marker>
</defs>
<!-- Single-line node (44px tall) -->
<g class="node c-blue" me more about this')">
<rect x="250" y="40" width="180" height="44" rx="8" stroke-width="0.5"/>
<text class="th" x="340" y="62" text-anchor="middle" dominant-baseline="central">Step one</text>
</g>
<!-- Connector arrow -->
<line x1="340" y1="84" x2="340" y2="120" class="arr" marker-end="url(#arrow)"/>
<!-- Two-line node (56px tall) -->
<g class="node c-teal" this step')">
<rect x="230" y="120" width="220" height="56" rx="8" stroke-width="0.5"/>
<text class="th" x="340" y="140" text-anchor="middle" dominant-baseline="central">Step two</text>
<text class="ts" x="340" y="158" text-anchor="middle" dominant-baseline="central">Processes the input</text>
</g>
</svg>
SVG rules:
- ViewBox always 680px wide (
viewBox="0 0 680 H"). Set H to fit content + 40px padding
- Safe area: x=40 to x=640, y=40 to y=(H-40)
- Pre-built classes:
t (14px), ts (12px secondary), th (14px medium 500), box, node, arr, c-{color}
- Every
<text> element must carry a class (t, ts, or th)
- Use
dominant-baseline="central" for vertical text centering in boxes
- Connector paths need
fill="none" (SVG defaults to fill: black)
- Stroke width: 0.5px for borders and edges
- Make all nodes clickable:
onclick="sendPrompt('...')"
Step 6: Interactive Explainer Template
For interactive explainers (sliders, live calculations, inline SVG):
<div style="display: flex; align-items: center; gap: 12px; margin: 0 0 1.5rem;">
<label style="font-size: 14px; color: var(--color-text-secondary);">Years</label>
<input type="range" min="1" max="40" value="20" id="years" style="flex: 1;" />
<span style="font-size: 14px; font-weight: 500; min-width: 24px;" id="years-out">20</span>
</div>
<div style="display: flex; align-items: baseline; gap: 8px; margin: 0 0 1.5rem;">
<span style="font-size: 14px; color: var(--color-text-secondary);">$1,000 →</span>
<span style="font-size: 24px; font-weight: 500;" id="result">$3,870</span>
</div>
<div style="margin: 2rem 0; position: relative; height: 240px;">
<canvas id="chart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.js"
<script>
function initChart() {
// slider logic, chart rendering, sendPrompt() for follow-ups
}
if (window.Chart) initChart();
</script>
Use sendPrompt() to let users ask follow-ups: sendPrompt('What if I increase the rate to 10%?')
Step 7: Respond to the User
After rendering the widget, briefly explain:
- What the widget shows
- How to interact with it (which controls do what)
- One key insight from the data
Keep it concise — the widget speaks for itself.
Reference Files
references/design_system.md — Complete color palette (9 ramps × 7 stops), CSS variables, UI component patterns, metric cards, layout rules
references/svg_and_diagrams.md — SVG viewBox setup, font calibration, pre-built classes, flowchart/structural/illustrative diagram patterns with examples
references/chart_js.md — Chart.js configuration, script load ordering, canvas sizing, legend patterns, dashboard layout
Read the relevant reference file when you need specific design tokens, SVG coordinate math, or Chart.js configuration details.
1---2name: generative-ui3description: Design system and guidelines for Claude's built-in generative UI — the show_widget tool that renders interactive HTML/SVG widgets inline in claude.ai conversations. This skill provides the complete Anthropic "Imagine" design system so Claude produces high-quality widgets without needing to call read_me first. Use this skill whenever the user asks to visualize data, create an interactive chart, build a dashboard, render a diagram, draw a flowchart, show a mockup, create an interactive explainer, or produce any visual content beyond plain text or markdown. Triggers include: "show me", "visualize", "draw", "chart", "dashboard", "diagram", "flowchart", "widget", "interactive", "mockup", "illustrate", "explain how X works" (with visual), or any request for visual/interactive output. Also triggers when the user wants to display financial data visually, create comparison grids, or build tools with sliders, toggles, or live-updating displays.4---5
6# Generative UI Skill
7
8This skill contains the complete design system for Claude's built-in `show_widget` tool — the generative UI feature that renders interactive HTML/SVG widgets inline in claude.ai conversations. The guidelines below are the actual Anthropic "Imagine — Visual Creation Suite" design rules, extracted so you can produce high-quality widgets directly without needing the `read_me` setup call.
9
10**How it works**: On claude.ai, Claude has access to the `show_widget` tool which renders raw HTML/SVG fragments inline in the conversation. This skill provides the design system, templates, and patterns to use it well.
11
12---
13
14## Step 1: Pick the Right Visual Type
15
16Route on the **verb**, not the noun. Same subject, different visual depending on what was asked:
17
18| User says | Type | Format |
19|---|---|---|
20| "how does X work" | Illustrative diagram | SVG |
21| "X architecture" | Structural diagram | SVG |
22| "what are the steps" | Flowchart | SVG |
23| "explain compound interest" | Interactive explainer | HTML |
24| "compare these options" | Comparison grid | HTML |
25| "show revenue chart" | Chart.js chart | HTML |
26| "create a contact card" | Data record | HTML |
27| "draw a sunset" | Art/illustration | SVG |
28
29---
30
31## Step 2: Build the Widget
32
33### Structure (strict order)
34
35```
36<style> → HTML content → <script>
37```
38
39Output streams token-by-token. Styles must exist before the elements they target, and scripts must run after the DOM is ready.
40
41### Philosophy
42
43- **Seamless**: Users shouldn't notice where the host UI ends and your widget begins
44- **Flat**: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces
45- **Compact**: Show the essential inline. Explain the rest in text
46- **Text goes in your response, visuals go in the tool** — all explanatory text, descriptions, and summaries must be written as normal response text OUTSIDE the tool call. The tool output should contain ONLY the visual element
47
48### Core Rules
49
50- No `<!-- comments -->` or `/* comments */` (waste tokens, break streaming)
51- No font-size below 11px
52- No emoji — use CSS shapes or SVG paths
53- No gradients, drop shadows, blur, glow, or neon effects
54- No dark/colored backgrounds on outer containers (transparent only — host provides the bg)
55- **Typography**: two weights only: 400 regular, 500 medium. Never use 600 or 700. Headings: h1=22px, h2=18px, h3=16px — all font-weight 500. Body text=16px, weight 400, line-height 1.7
56- **Sentence case** always. Never Title Case, never ALL CAPS
57- No mid-sentence bolding — entity names go in `code style` not **bold**
58- No `<!DOCTYPE>`, `<html>`, `<head>`, or `<body>` — just content fragments
59- No `position: fixed` — use normal-flow layouts
60- No tabs, carousels, or `display: none` sections during streaming
61- No nested scrolling — auto-fit height
62- Corners: `border-radius: var(--border-radius-lg)` for cards, `var(--border-radius-md)` for elements
63- No rounded corners on single-sided borders (border-left, border-top)
64- **Round every displayed number** — use `Math.round()`, `.toFixed(n)`, or `Intl.NumberFormat`
65
66### CDN Allowlist (CSP-enforced)
67
68External resources may ONLY load from:
69- `cdnjs.cloudflare.com`
70- `cdn.jsdelivr.net`
71- `unpkg.com`
72- `esm.sh`
73
74All other origins are blocked — the request silently fails.
75
76### CSS Variables
77
78**Backgrounds**: `--color-background-primary` (white), `-secondary` (surfaces), `-tertiary` (page bg), `-info`, `-danger`, `-success`, `-warning`
79**Text**: `--color-text-primary` (black), `-secondary` (muted), `-tertiary` (hints), `-info`, `-danger`, `-success`, `-warning`
80**Borders**: `--color-border-tertiary` (0.15α, default), `-secondary` (0.3α, hover), `-primary` (0.4α), semantic `-info/-danger/-success/-warning`
81**Typography**: `--font-sans`, `--font-serif`, `--font-mono`
82**Layout**: `--border-radius-md` (8px), `--border-radius-lg` (12px), `--border-radius-xl` (16px)
83
84All auto-adapt to light/dark mode.
85
86**Dark mode is mandatory** — every color must work in both modes:
87- In HTML: always use CSS variables for text. Never hardcode colors like `color: #333`
88- In SVG: use pre-built color classes (`c-blue`, `c-teal`, etc.) — they handle light/dark automatically
89- Mental test: if the background were near-black, would every text element still be readable?
90
91### `sendPrompt(text)`
92
93A 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.
94
95---
96
97## Step 3: Render with `show_widget`
98
99The `show_widget` tool is built into claude.ai — no activation needed. Pass your widget code directly:
100
101```json
102{
103 "title": "snake_case_widget_name",
104 "widget_code": "<style>...</style>\n<div>...</div>\n<script>...</script>"
105}
106```
107
108| Parameter | Type | Required | Description |
109|---|---|---|---|
110| `title` | string | Yes | Snake_case identifier for the widget |
111| `widget_code` | string | Yes | HTML or SVG code. For SVG: start with `<svg>`. For HTML: content fragment |
112
113For SVG output: start `widget_code` with `<svg` — it will be auto-detected and wrapped appropriately.
114
115---
116
117## Step 4: Chart.js Template
118
119For charts, use `onload` callback pattern to handle script load ordering:
120
121```html
122<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px;">
123 <div style="background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 1rem;">
124 <div style="font-size: 13px; color: var(--color-text-secondary);">Label</div>
125 <div style="font-size: 24px; font-weight: 500;" id="stat1">—</div>
126 </div>
127</div>
128
129<div style="position: relative; width: 100%; height: 300px; margin-top: 1rem;">
130 <canvas id="myChart"></canvas>
131</div>
132
133<div style="display: flex; align-items: center; gap: 12px; margin-top: 1rem;">
134 <label style="font-size: 14px; color: var(--color-text-secondary);">Parameter</label>
135 <input type="range" min="0" max="100" value="50" id="param" step="1" style="flex: 1;" />
136 <span style="font-size: 14px; font-weight: 500; min-width: 32px;" id="param-out">50</span>
137</div>
138
139<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.js" onload="initChart()"></script>
140<script>
141function initChart() {
142 const slider = document.getElementById('param');
143 const out = document.getElementById('param-out');
144 let chart = null;
145
146 function update() {
147 const val = parseFloat(slider.value);
148 out.textContent = val;
149 document.getElementById('stat1').textContent = val.toFixed(1);
150
151 const labels = [], data = [];
152 for (let x = 0; x <= 100; x++) {
153 labels.push(x);
154 data.push(x * val / 100);
155 }
156
157 if (chart) chart.destroy();
158 chart = new Chart(document.getElementById('myChart'), {
159 type: 'line',
160 data: { labels, datasets: [{ data, borderColor: '#7F77DD', borderWidth: 2, pointRadius: 0, fill: false }] },
161 options: {
162 responsive: true,
163 maintainAspectRatio: false,
164 plugins: { legend: { display: false } },
165 scales: { x: { grid: { display: false } } }
166 }
167 });
168 }
169
170 slider.addEventListener('input', update);
171 update();
172}
173if (window.Chart) initChart();
174</script>
175```
176
177**Chart.js rules:**
178- Canvas cannot resolve CSS variables — use hardcoded hex
179- Set height ONLY on the wrapper div, never on canvas itself
180- Always `responsive: true, maintainAspectRatio: false`
181- Always disable default legend, build custom HTML legends
182- Number formatting: `-$5M` not `$-5M` (negative sign before currency symbol)
183- Use `onload="initChart()"` on CDN script tag + `if (window.Chart) initChart();` as fallback
184
185---
186
187## Step 5: SVG Diagram Template
188
189For flowcharts and diagrams, use SVG with pre-built classes:
190
191```svg
192<svg width="100%" viewBox="0 0 680 H">
193 <defs>
194 <marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
195 <path d="M2 1L8 5L2 9" fill="none" stroke="context-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
196 </marker>
197 </defs>
198
199 <!-- Single-line node (44px tall) -->
200 <g class="node c-blue" onclick="sendPrompt('Tell me more about this')">
201 <rect x="250" y="40" width="180" height="44" rx="8" stroke-width="0.5"/>
202 <text class="th" x="340" y="62" text-anchor="middle" dominant-baseline="central">Step one</text>
203 </g>
204
205 <!-- Connector arrow -->
206 <line x1="340" y1="84" x2="340" y2="120" class="arr" marker-end="url(#arrow)"/>
207
208 <!-- Two-line node (56px tall) -->
209 <g class="node c-teal" onclick="sendPrompt('Explain this step')">
210 <rect x="230" y="120" width="220" height="56" rx="8" stroke-width="0.5"/>
211 <text class="th" x="340" y="140" text-anchor="middle" dominant-baseline="central">Step two</text>
212 <text class="ts" x="340" y="158" text-anchor="middle" dominant-baseline="central">Processes the input</text>
213 </g>
214</svg>
215```
216
217**SVG rules:**
218- ViewBox always 680px wide (`viewBox="0 0 680 H"`). Set H to fit content + 40px padding
219- Safe area: x=40 to x=640, y=40 to y=(H-40)
220- Pre-built classes: `t` (14px), `ts` (12px secondary), `th` (14px medium 500), `box`, `node`, `arr`, `c-{color}`
221- Every `<text>` element must carry a class (`t`, `ts`, or `th`)
222- Use `dominant-baseline="central"` for vertical text centering in boxes
223- Connector paths need `fill="none"` (SVG defaults to `fill: black`)
224- Stroke width: 0.5px for borders and edges
225- Make all nodes clickable: `onclick="sendPrompt('...')"`
226
227---
228
229## Step 6: Interactive Explainer Template
230
231For interactive explainers (sliders, live calculations, inline SVG):
232
233```html
234<div style="display: flex; align-items: center; gap: 12px; margin: 0 0 1.5rem;">
235 <label style="font-size: 14px; color: var(--color-text-secondary);">Years</label>
236 <input type="range" min="1" max="40" value="20" id="years" style="flex: 1;" />
237 <span style="font-size: 14px; font-weight: 500; min-width: 24px;" id="years-out">20</span>
238</div>
239
240<div style="display: flex; align-items: baseline; gap: 8px; margin: 0 0 1.5rem;">
241 <span style="font-size: 14px; color: var(--color-text-secondary);">$1,000 →</span>
242 <span style="font-size: 24px; font-weight: 500;" id="result">$3,870</span>
243</div>
244
245<div style="margin: 2rem 0; position: relative; height: 240px;">
246 <canvas id="chart"></canvas>
247</div>
248
249<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.5.1/chart.umd.js" onload="initChart()"></script>
250<script>
251function initChart() {
252 // slider logic, chart rendering, sendPrompt() for follow-ups
253}
254if (window.Chart) initChart();
255</script>
256```
257
258Use `sendPrompt()` to let users ask follow-ups: `sendPrompt('What if I increase the rate to 10%?')`
259
260---
261
262## Step 7: Respond to the User
263
264After rendering the widget, briefly explain:
2651. What the widget shows
2662. How to interact with it (which controls do what)
2673. One key insight from the data
268
269Keep it concise — the widget speaks for itself.
270
271---
272
273## Reference Files
274
275- `references/design_system.md` — Complete color palette (9 ramps × 7 stops), CSS variables, UI component patterns, metric cards, layout rules
276- `references/svg_and_diagrams.md` — SVG viewBox setup, font calibration, pre-built classes, flowchart/structural/illustrative diagram patterns with examples
277- `references/chart_js.md` — Chart.js configuration, script load ordering, canvas sizing, legend patterns, dashboard layout
278
279Read the relevant reference file when you need specific design tokens, SVG coordinate math, or Chart.js configuration details.