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.4.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.4.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---56# Generative UI Skill78This 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.910**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.1112---1314## Step 1: Pick the Right Visual Type1516Route on the **verb**, not the noun. Same subject, different visual depending on what was asked:1718| 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 |2829---3031## Step 2: Build the Widget3233### Structure (strict order)3435```36<style> → HTML content → <script>37```3839Output streams token-by-token. Styles must exist before the elements they target, and scripts must run after the DOM is ready.4041### Philosophy4243- **Seamless**: Users shouldn't notice where the host UI ends and your widget begins44- **Flat**: No gradients, mesh backgrounds, noise textures, or decorative effects. Clean flat surfaces45- **Compact**: Show the essential inline. Explain the rest in text46- **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 element4748### Core Rules4950- No `<!-- comments -->` or `/* comments */` (waste tokens, break streaming)51- No font-size below 11px52- No emoji — use CSS shapes or SVG paths53- No gradients, drop shadows, blur, glow, or neon effects54- 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.756- **Sentence case** always. Never Title Case, never ALL CAPS57- No mid-sentence bolding — entity names go in `code style` not **bold**58- No `<!DOCTYPE>`, `<html>`, `<head>`, or `<body>` — just content fragments59- No `position: fixed` — use normal-flow layouts60- No tabs, carousels, or `display: none` sections during streaming61- No nested scrolling — auto-fit height62- Corners: `border-radius: var(--border-radius-lg)` for cards, `var(--border-radius-md)` for elements63- No rounded corners on single-sided borders (border-left, border-top)64- **Round every displayed number** — use `Math.round()`, `.toFixed(n)`, or `Intl.NumberFormat`6566### CDN Allowlist (CSP-enforced)6768External resources may ONLY load from:69- `cdnjs.cloudflare.com`70- `cdn.jsdelivr.net`71- `unpkg.com`72- `esm.sh`7374All other origins are blocked — the request silently fails.7576### CSS Variables7778**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)8384All auto-adapt to light/dark mode.8586**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 automatically89- Mental test: if the background were near-black, would every text element still be readable?9091### `sendPrompt(text)`9293A 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.9495---9697## Step 3: Render with `show_widget`9899The `show_widget` tool is built into claude.ai — no activation needed. Pass your widget code directly:100101```json102{103 "title": "snake_case_widget_name",104 "widget_code": "<style>...</style>\n<div>...</div>\n<script>...</script>"105}106```107108| 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 |112113For SVG output: start `widget_code` with `<svg` — it will be auto-detected and wrapped appropriately.114115---116117## Step 4: Chart.js Template118119For charts, use `onload` callback pattern to handle script load ordering:120121```html122<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>128129<div style="position: relative; width: 100%; height: 300px; margin-top: 1rem;">130 <canvas id="myChart"></canvas>131</div>132133<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>138139<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.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;145146 function update() {147 const val = parseFloat(slider.value);148 out.textContent = val;149 document.getElementById('stat1').textContent = val.toFixed(1);150151 const labels = [], data = [];152 for (let x = 0; x <= 100; x++) {153 labels.push(x);154 data.push(x * val / 100);155 }156157 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 }169170 slider.addEventListener('input', update);171 update();172}173if (window.Chart) initChart();174</script>175```176177**Chart.js rules:**178- Canvas cannot resolve CSS variables — use hardcoded hex179- Set height ONLY on the wrapper div, never on canvas itself180- Always `responsive: true, maintainAspectRatio: false`181- Always disable default legend, build custom HTML legends182- Number formatting: `-$5M` not `$-5M` (negative sign before currency symbol)183- Use `onload="initChart()"` on CDN script tag + `if (window.Chart) initChart();` as fallback184185---186187## Step 5: SVG Diagram Template188189For flowcharts and diagrams, use SVG with pre-built classes:190191```svg192<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>198199 <!-- 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>204205 <!-- Connector arrow -->206 <line x1="340" y1="84" x2="340" y2="120" class="arr" marker-end="url(#arrow)"/>207208 <!-- 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```216217**SVG rules:**218- ViewBox always 680px wide (`viewBox="0 0 680 H"`). Set H to fit content + 40px padding219- 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 boxes223- Connector paths need `fill="none"` (SVG defaults to `fill: black`)224- Stroke width: 0.5px for borders and edges225- Make all nodes clickable: `onclick="sendPrompt('...')"`226227---228229## Step 6: Interactive Explainer Template230231For interactive explainers (sliders, live calculations, inline SVG):232233```html234<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>239240<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>244245<div style="margin: 2rem 0; position: relative; height: 240px;">246 <canvas id="chart"></canvas>247</div>248249<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js" onload="initChart()"></script>250<script>251function initChart() {252 // slider logic, chart rendering, sendPrompt() for follow-ups253}254if (window.Chart) initChart();255</script>256```257258Use `sendPrompt()` to let users ask follow-ups: `sendPrompt('What if I increase the rate to 10%?')`259260---261262## Step 7: Respond to the User263264After rendering the widget, briefly explain:2651. What the widget shows2662. How to interact with it (which controls do what)2673. One key insight from the data268269Keep it concise — the widget speaks for itself.270271---272273## Reference Files274275- `references/design_system.md` — Complete color palette (9 ramps × 7 stops), CSS variables, UI component patterns, metric cards, layout rules276- `references/svg_and_diagrams.md` — SVG viewBox setup, font calibration, pre-built classes, flowchart/structural/illustrative diagram patterns with examples277- `references/chart_js.md` — Chart.js configuration, script load ordering, canvas sizing, legend patterns, dashboard layout278279Read the relevant reference file when you need specific design tokens, SVG coordinate math, or Chart.js configuration details.