
Generative Ui
- 1.8k installs
- 3.1k repo stars
- Updated July 21, 2026
- himself65/finance-skills
generative-ui is the Imagine design system skill for claude.ai show_widget interactive HTML and SVG widgets.
About
The generative-ui skill provides Anthropic Imagine design system guidelines for claude.ai show_widget tool rendering interactive HTML and SVG inline in conversations. Step one routes on user verb: how does X work maps to illustrative SVG diagrams, compare options to HTML comparison grids, revenue charts to Chart.js HTML, and compound interest to interactive HTML explainers. Widget structure is strict style then HTML then script for token streaming order. Philosophy demands seamless flat compact widgets without gradients, mesh backgrounds, drop shadows, blur, glow, emoji, or dark outer container backgrounds. Typography uses only weights 400 and 500 with h1 22px, h2 18px, h3 16px, body 16px line-height 1.7, sentence case always. CDN allowlist restricts external scripts to cdnjs, jsdelivr, unpkg, and esm.sh only. CSS variables cover backgrounds, text, borders, fonts, and border-radius adapting to light and dark mode. Text explanations belong in normal response outside the tool; tool output is visual only.
- Routes visual type by user verb: diagram, chart, comparison, or explainer.
- Widget order: style block, HTML content, then script for streaming.
- Flat design: no gradients, shadows, emoji, or hardcoded colors.
- CDN allowlist: cdnjs, jsdelivr, unpkg, esm.sh only.
- Explanatory text stays outside show_widget; tool output is visual only.
Generative Ui by the numbers
- 1,802 all-time installs (skills.sh)
- +127 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #204 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
generative-ui capabilities & compatibility
- Capabilities
- visual type routing table · streaming safe widget structure · imagine typography and color rules · cdn csp allowlist · dark mode css variable usage
- Use cases
- ui design · marketing
What generative-ui says it does
No gradients, drop shadows, blur, glow, or neon effects
npx skills add https://github.com/himself65/finance-skills --skill generative-uiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.8k |
|---|---|
| repo stars | ★ 3.1k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 21, 2026 |
| Repository | himself65/finance-skills ↗ |
How do I create an inline chart, diagram, or comparison widget that follows Anthropic design rules?
Produce claude.ai show_widget HTML and SVG widgets following the Imagine design system for charts and explainers.
Who is it for?
Claude producing visual widgets for charts, diagrams, dashboards, and interactive explainers.
Skip if: Skip for plain markdown text answers without any visual widget requirement.
When should I use this skill?
User asks to visualize, chart, diagram, dashboard, mockup, or illustrate interactively.
What you get
CSP-compliant flat widget with correct typography, CSS variables, and streaming-safe structure.
- Inline widget HTML/SVG
- Dashboard components
Files
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 stylenot bold - No
<!DOCTYPE>,<html>,<head>, or<body>— just content fragments - No
position: fixed— use normal-flow layouts - No tabs, carousels, or
display: nonesections 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), orIntl.NumberFormat
CDN Allowlist (CSP-enforced)
External resources may ONLY load from:
cdnjs.cloudflare.comcdn.jsdelivr.netunpkg.comesm.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" onload="initChart()"></script>
<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:
-$5Mnot$-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" onclick="sendPrompt('Tell 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" onclick="sendPrompt('Explain 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, orth) - Use
dominant-baseline="central"for vertical text centering in boxes - Connector paths need
fill="none"(SVG defaults tofill: 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" onload="initChart()"></script>
<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: 1. What the widget shows 2. How to interact with it (which controls do what) 3. 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 rulesreferences/svg_and_diagrams.md— SVG viewBox setup, font calibration, pre-built classes, flowchart/structural/illustrative diagram patterns with examplesreferences/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.
generative-ui
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.
What it does
Provides the complete Anthropic "Imagine" design system so Claude produces high-quality widgets without needing to call read_me first. Covers:
- Charts — Chart.js line, bar, area charts with interactive controls
- Diagrams — SVG flowcharts, structural diagrams, illustrative diagrams
- Dashboards — metric cards, comparison grids, data displays
- Interactive explainers — sliders, toggles, live-updating calculations
- Design tokens — CSS variables, color palette (light/dark), typography, spacing
Key design principles
- Seamless — widgets blend with the host UI
- Flat — no gradients, shadows, or decorative effects
- Compact — show the essential inline, explain in text
- Dark mode mandatory — all colors work in both light and dark mode via CSS variables
Triggers
- "show me", "visualize", "draw", "chart", "dashboard"
- "diagram", "flowchart", "widget", "interactive", "mockup"
- "explain how X works" (with visual), "illustrate"
- Any request for visual/interactive output beyond plain text or markdown
Platform
Works on Claude.ai (built-in show_widget tool).
Setup
# As a plugin (recommended — installs all skills)
npx plugins add himself65/finance-skills --plugin finance-ui-tools
# Or install just this skill
npx skills add himself65/finance-skills --skill generative-uiSee the main README for more installation options.
Reference files
references/design_system.md— Complete color palette, CSS variables, UI component patterns, metric cards, layout rulesreferences/svg_and_diagrams.md— SVG viewBox setup, font calibration, pre-built classes, diagram patterns with examplesreferences/chart_js.md— Chart.js configuration, script load ordering, canvas sizing, legend patterns, dashboard layout
Chart.js Reference
Extracted from Claude's actual visualize:read_me guidelines.
---
Basic Setup
<div style="position: relative; width: 100%; height: 300px;">
<canvas id="myChart"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js" onload="initChart()"></script>
<script>
function initChart() {
new Chart(document.getElementById('myChart'), {
type: 'bar',
data: { labels: ['Q1','Q2','Q3','Q4'], datasets: [{ label: 'Revenue', data: [12,19,8,15] }] },
options: { responsive: true, maintainAspectRatio: false }
});
}
if (window.Chart) initChart();
</script>---
Rules
Canvas Sizing
- Set height ONLY on the wrapper div, never on the canvas element itself
- Use
position: relativeon the wrapper - Use
responsive: true, maintainAspectRatio: falsein Chart.js options - Never set CSS height directly on canvas — causes wrong dimensions, especially for horizontal bar charts
- For horizontal bar charts: wrapper div height = at least
(number_of_bars × 40) + 80pixels
Script Load Ordering
- Load UMD build via
<script src="https://cdnjs.cloudflare.com/ajax/libs/...">— setswindow.Chartglobal - Follow with plain
<script>(notype="module") - CDN scripts may not be loaded when the next
<script>runs (especially during streaming) - Always use `onload="initChart()"` on the CDN script tag
- Define your chart init in a named function
- Add
if (window.Chart) initChart();as fallback at end of inline script - This guarantees charts render regardless of load order
Canvas and CSS Variables
- Canvas cannot resolve CSS variables. Use hardcoded hex or Chart.js defaults
- Multiple charts: use unique IDs (
myChart1,myChart2). Each gets its own canvas+div pair
Scale Padding
- For bubble and scatter charts: bubble radii extend past center points, so points near axis boundaries get clipped
- Pad the scale range — set
scales.y.minandscales.y.max~10% beyond data range - Or use
layout: { padding: 20 }as a blunt fallback
X-Axis Labels
- Chart.js auto-skips x-axis labels when they'd overlap
- For ≤12 categories where all labels must be visible (waterfall, monthly), set
scales.x.ticks: { autoSkip: false, maxRotation: 45 }
---
Number Formatting
Negative values are -$5M not $-5M — sign before currency symbol.
Use a formatter:
(v) => (v < 0 ? '-' : '') + '$' + Math.abs(v) + 'M'---
Legends
Always disable Chart.js default and build custom HTML:
plugins: { legend: { display: false } }<div style="display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px; font-size: 12px; color: var(--color-text-secondary);">
<span style="display: flex; align-items: center; gap: 4px;">
<span style="width: 10px; height: 10px; border-radius: 2px; background: #3266ad;"></span>Chrome 65%
</span>
<span style="display: flex; align-items: center; gap: 4px;">
<span style="width: 10px; height: 10px; border-radius: 2px; background: #73726c;"></span>Safari 18%
</span>
</div>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.
---
Dashboard Layout
Wrap summary numbers in metric cards above the chart:
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 12px; margin-bottom: 1rem;">
<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);">Revenue</div>
<div style="font-size: 24px; font-weight: 500;">$2.4M</div>
</div>
<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);">Growth</div>
<div style="font-size: 24px; font-weight: 500; color: var(--color-text-success);">+12%</div>
</div>
</div>
<div style="position: relative; width: 100%; height: 300px;">
<canvas id="revenueChart"></canvas>
</div>Chart canvas flows below without a card wrapper. Use sendPrompt() for drill-down: sendPrompt('Break down Q4 by region').
---
ERD / Database Schemas (mermaid.js)
Use mermaid.js erDiagram, not Chart.js or SVG:
<style>
#erd svg.erDiagram .row-rect-odd path,
#erd svg.erDiagram .row-rect-odd rect,
#erd svg.erDiagram .row-rect-even path,
#erd svg.erDiagram .row-rect-even rect { stroke: none !important; }
</style>
<div id="erd"></div>
<script type="module">
import mermaid from 'https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs';
const dark = matchMedia('(prefers-color-scheme: dark)').matches;
await document.fonts.ready;
mermaid.initialize({
startOnLoad: false,
theme: 'base',
themeVariables: {
darkMode: dark,
fontSize: '13px',
lineColor: dark ? '#9c9a92' : '#73726c',
textColor: dark ? '#c2c0b6' : '#3d3d3a',
},
});
const { svg } = await mermaid.render('erd-svg', `erDiagram
USERS ||--o{ POSTS : writes
POSTS ||--o{ COMMENTS : has`);
document.getElementById('erd').innerHTML = svg;
</script>Generative UI Design System
Extracted from Claude's actual visualize:read_me guidelines (Imagine — Visual Creation Suite).
---
Color Palette
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.
| Class | Ramp | 50 | 100 | 200 | 400 | 600 | 800 | 900 |
|---|---|---|---|---|---|---|---|---|
c-purple | Purple | #EEEDFE | #CECBF6 | #AFA9EC | #7F77DD | #534AB7 | #3C3489 | #26215C |
c-teal | Teal | #E1F5EE | #9FE1CB | #5DCAA5 | #1D9E75 | #0F6E56 | #085041 | #04342C |
c-coral | Coral | #FAECE7 | #F5C4B3 | #F0997B | #D85A30 | #993C1D | #712B13 | #4A1B0C |
c-pink | Pink | #FBEAF0 | #F4C0D1 | #ED93B1 | #D4537E | #993556 | #72243E | #4B1528 |
c-gray | Gray | #F1EFE8 | #D3D1C7 | #B4B2A9 | #888780 | #5F5E5A | #444441 | #2C2C2A |
c-blue | Blue | #E6F1FB | #B5D4F4 | #85B7EB | #378ADD | #185FA5 | #0C447C | #042C53 |
c-green | Green | #EAF3DE | #C0DD97 | #97C459 | #639922 | #3B6D11 | #27500A | #173404 |
c-amber | Amber | #FAEEDA | #FAC775 | #EF9F27 | #BA7517 | #854F0B | #633806 | #412402 |
c-red | Red | #FCEBEB | #F7C1C1 | #F09595 | #E24B4A | #A32D2D | #791F1F | #501313 |
How to Assign Colors
Color encodes meaning, not sequence. Don't cycle through colors like a rainbow.
- Group nodes by category — all nodes of the same type share one color
- Use gray for neutral/structural nodes (start, end, generic steps)
- Use 2-3 colors per diagram, not 6+. More = more visual noise
- Prefer purple, teal, coral, pink for general categories. Reserve blue, green, amber, red for semantic meaning (info, success, warning, error)
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, use two different stops:
- Light mode: 50 fill + 600 stroke + 800 title / 600 subtitle
- Dark mode: 800 fill + 200 stroke + 100 title / 200 subtitle
Example: text on Blue 50 (#E6F1FB) must use Blue 800 (#0C447C) or 900 (#042C53), not black.
---
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 — preferred for most components), --border-radius-xl (16px)
All auto-adapt to light/dark mode. For custom colors in HTML, use CSS variables. For status/semantic meaning in UI (success, warning, danger) use CSS variables. For categorical coloring in both diagrams and UI, use the color ramps.
---
UI Component Patterns
Aesthetic
Flat, clean, white surfaces. Minimal 0.5px borders. Generous whitespace. No gradients, no shadows (except functional focus rings). Everything should feel native to the host UI.
Tokens
- Borders: always
0.5px solid var(--color-border-tertiary)(or-secondaryfor emphasis) - Corner radius:
var(--border-radius-md)for most elements,var(--border-radius-lg)for cards - Cards: white bg (
var(--color-background-primary)), 0.5px border, radius-lg, padding 1rem 1.25rem - Form elements (input, select, textarea, button, range slider) are pre-styled — write bare tags
- Buttons: transparent bg, 0.5px border-secondary, hover bg-secondary, active scale(0.98). If it triggers
sendPrompt, append a ↗ arrow - Spacing: use rem for vertical rhythm (1rem, 1.5rem, 2rem), px for component-internal gaps (8px, 12px, 16px)
- Box-shadows: none, except
box-shadow: 0 0 0 Npxfocus rings on inputs
Metric Cards
For summary numbers (revenue, count, percentage):
<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;">$1,234</div>
</div>Use in grids of 2-4 with gap: 12px. Distinct from raised cards (which have white bg + border).
Layout Patterns
- Editorial (explanatory content): no card wrapper, prose flows naturally
- Card (bounded objects like a contact record, receipt): single raised card wraps the whole thing
- Don't put tables in widgets — output them as markdown in your response text
Grid overflow: grid-template-columns: 1fr has min-width: auto by default. Use minmax(0, 1fr) to clamp.
Interactive Explainer
Sliders, buttons, live state displays, charts. Keep prose explanations in your response text. No card wrapper. Whitespace is the container.
<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>Comparison Grid
Side-by-side card grid. Highlight differences with semantic colors. Use repeat(auto-fit, minmax(160px, 1fr)) for responsive columns. When one option is recommended, accent its card with border: 2px solid var(--color-border-info) (the only exception to the 0.5px rule).
Data Record
Wrap in a single raised card. Example:
<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;">
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 16px;">
<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>
<div>
<p style="font-weight: 500; font-size: 15px; margin: 0;">Maya Rodriguez</p>
<p style="font-size: 13px; color: var(--color-text-secondary); margin: 0;">VP of Engineering</p>
</div>
</div>
</div>---
Complexity Budget (Hard Limits)
- Box subtitles: ≤5 words
- Colors: ≤2 ramps per diagram
- 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
SVG Setup and Diagram Patterns
Extracted from Claude's actual visualize:read_me guidelines.
---
SVG Setup
ViewBox: <svg width="100%" viewBox="0 0 680 H"> — 680px wide, flexible height. Set H to fit content tightly (last element's bottom edge + 40px padding). Safe area: x=40 to x=640, y=40 to y=(H-40). Background transparent.
The 680 in viewBox is load-bearing — do not change it. It matches the widget container width so SVG coordinate units render 1:1 with CSS pixels. If your diagram content is naturally narrow, keep viewBox width at 680 and center the content — do not shrink the viewBox.
Do not wrap the SVG in a container `<div>` with a background color — the widget host provides the card container and background. Output the raw <svg> element directly.
ViewBox Safety Checklist
Before finalizing any SVG, verify: 1. Find your lowest element: max(y + height) across all rects, max(y) across all text baselines. Set viewBox height = that value + 40px buffer 2. Find your rightmost element: max(x + width) across all rects. All content must stay within x=0 to x=680 3. For text with text-anchor="end", the text extends LEFT from x. If x=118 and text is 200px wide, it starts at x=-82 — outside the viewBox 4. Never use negative x or y coordinates. The viewBox starts at 0,0 5. For every pair of boxes in the same row, check that left box's (x + width) < right box's x by at least 20px
Font Size Calibration
| Text | Chars | Weight | Size | Rendered Width |
|---|---|---|---|---|
| Authentication Service | 22 | 500 | 14px | 167px |
| Background Job Processor | 24 | 500 | 14px | 201px |
| Detects and validates incoming tokens | 37 | 400 | 14px | 279px |
| forwards request to | 19 | 400 | 12px | 123px |
Before placing text in a box: does (text width + 2×padding) fit the container? Box width formula: rect_width = max(title_chars × 8, subtitle_chars × 7) + 24.
SVG <text> never auto-wraps. Every line break needs an explicit <tspan x="..." dy="1.2em">.
Pre-built Classes
Already loaded in SVG widget context:
class="t"= sans 14px primary textclass="ts"= sans 12px secondary textclass="th"= sans 14px medium (500) heading textclass="box"= neutral rect (bg-secondary fill, border stroke)class="node"= clickable group with hover effect (cursor pointer, slight dim on hover)class="arr"= arrow line (1.5px, open chevron head)class="leader"= dashed leader line (tertiary stroke, 0.5px, dashed)class="c-{ramp}"= colored node. Apply to<g>or shape element (rect/circle/ellipse), NOT to paths. Sets fill+stroke on shapes, auto-adjusts child text classes, dark mode automatic- Short aliases:
var(--p),var(--s),var(--t),var(--bg2),var(--b)
`c-{ramp}` nesting: These classes use direct-child selectors. Nest a <g> inside a <g class="c-blue"> and inner shapes become grandchildren — they lose the fill and render BLACK. Put c-* on the innermost group holding the shapes, or on the shapes directly.
Arrow Marker (always include)
<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>Use marker-end="url(#arrow)" on lines. The head uses context-stroke — inherits the color of whichever line it sits on.
Style Rules
- Every
<text>element must carry one of:t,ts,th - Use only two font sizes: 14px (node labels) and 12px (subtitles, descriptions, arrow labels)
- No decorative step numbers or oversized headings
- No icons or illustrations inside boxes — text only
- Sentence case on all labels
- Stroke width: 0.5px for diagram borders and edges
- Connector paths need
fill="none"(SVG defaults tofill: black) rx="4"for subtle corners,rx="8"max for emphasized rounding- One SVG per tool call — never leave an abandoned or partial SVG
---
Diagram Types
Flowchart
For sequential processes, cause-and-effect, decision trees.
Planning: Size boxes to fit text generously. At 14px, each character is ~8px wide. A label like "Load Balancer" (13 chars) needs at least 140px wide rect.
Spacing: 60px minimum between boxes, 24px padding inside boxes, 12px between text and edges. Leave 10px gap between arrowheads and box edges. Two-line boxes need at least 56px height with 22px between lines.
Vertical text placement: Every <text> inside a box needs dominant-baseline="central", with y set to the center of its slot. Formula: for text centered in a rect at (x, y, w, h), use <text x={x+w/2} y={y+h/2} text-anchor="middle" dominant-baseline="central">.
Layout: Prefer single-direction flows. Max 4-5 nodes per diagram. The widget is narrow (~680px).
Single-line node (44px tall):
<g class="node c-blue" onclick="sendPrompt('Tell me more about T-cells')">
<rect x="100" y="20" width="180" height="44" rx="8" stroke-width="0.5"/>
<text class="th" x="190" y="42" text-anchor="middle" dominant-baseline="central">T-cells</text>
</g>Two-line node (56px tall):
<g class="node c-blue" onclick="sendPrompt('Tell me more about dendritic cells')">
<rect x="100" y="20" width="200" height="56" rx="8" stroke-width="0.5"/>
<text class="th" x="200" y="38" text-anchor="middle" dominant-baseline="central">Dendritic cells</text>
<text class="ts" x="200" y="56" text-anchor="middle" dominant-baseline="central">Detect foreign antigens</text>
</g>Connector (no label):
<line x1="200" y1="76" x2="200" y2="120" class="arr" marker-end="url(#arrow)"/>Arrows: Must not cross any other box or label. If the direct path crosses something, route around with an L-bend: <path d="M x1 y1 L x1 ymid L x2 ymid L x2 y2"/>.
Cycles: Don't draw as rings. Build a stepper in HTML instead: one panel per stage, dots showing position (● ○ ○), Next wraps from last stage to first.
Over budget prompts: If user lists 6+ components, decompose into a stripped overview + one diagram per interesting sub-flow, each with 3-4 nodes.
Structural Diagram
For concepts where physical or logical containment matters.
Container rules:
- Outermost: large rounded rect, rx=20-24, lightest fill (50 stop), 0.5px stroke (600 stop). Label at top-left, 14px bold
- Inner regions: medium rounded rects, rx=8-12, next shade fill (100-200 stop). Different color ramp if semantically different
- 20px minimum padding inside every container
- Max 2-3 nesting levels
Example (horizontal layout with two inner regions):
<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>
<g class="c-green">
<rect x="120" y="30" width="560" height="260" rx="20" stroke-width="0.5"/>
<text class="th" x="400" y="62" text-anchor="middle">Library branch</text>
<text class="ts" x="400" y="80" text-anchor="middle">Main floor</text>
</g>
<g class="c-teal">
<rect x="150" y="100" width="220" height="160" rx="12" stroke-width="0.5"/>
<text class="th" x="260" y="130" text-anchor="middle">Circulation desk</text>
<text class="ts" x="260" y="148" text-anchor="middle">Checkouts, returns</text>
</g>
<g class="c-amber">
<rect x="450" y="100" width="210" height="160" rx="12" stroke-width="0.5"/>
<text class="th" x="555" y="130" text-anchor="middle">Reading room</text>
<text class="ts" x="555" y="148" text-anchor="middle">Seating, reference</text>
</g>
<text class="ts" x="410" y="175" text-anchor="middle">Books</text>
<line x1="370" y1="185" x2="448" y2="185" class="arr" marker-end="url(#arrow)"/>Color in structural diagrams: Nested regions need distinct ramps. Same class on parent and child gives identical fills and flattens the hierarchy. Pick a related ramp for inner structures and a contrasting ramp for functionally different regions.
Database schemas / ERDs: Use mermaid.js, not SVG.
Illustrative Diagram
For building intuition. Draw the mechanism, not a diagram about the mechanism.
Two flavors:
- Physical subjects: simplified cross-sections, cutaways, schematics (a water heater is a tank with a burner)
- Abstract subjects: spatial metaphors (a transformer is stacked slabs with attention threads, a hash function is a funnel scattering into buckets)
What changes from flowchart rules:
- Shapes are freeform:
<path>,<ellipse>,<circle>,<polygon>, curved lines - Layout follows the subject's geometry, not a grid
- Color encodes intensity, not category (warm = active/high-weight, cool = dormant)
- Layering and overlap are encouraged for shapes (but never let a stroke cross text)
- Small shape-based indicators are allowed (triangles for flames, circles for bubbles)
- One gradient per diagram is permitted — only for continuous physical properties
- CSS
@keyframesanimation permitted (onlytransformandopacity, wrap in@media (prefers-reduced-motion: no-preference))
Prefer interactive over static: if the real-world system has a control, give the diagram that control. Use show_widget with inline SVG + HTML controls.
Label placement: Place labels outside the drawn object with thin leader lines (0.5px dashed). Reserve at least 140px of horizontal margin on the label side.
Composition approach: 1. Main object's silhouette — largest shape, centered 2. Internal structure: chambers, pipes, membranes 3. External connections: pipes, arrows, input/output labels 4. State indicators last: color fills, small animated elements 5. Leave generous whitespace around the object for labels
Routing Decisions
| User says | Type | What to draw |
|---|---|---|
| "how do LLMs work" | Illustrative | Token row, stacked layers, attention threads |
| "transformer architecture" | Structural | Labelled boxes: embedding, attention, FFN |
| "how does attention work" | Illustrative | One query token, fan of lines to every key |
| "what are the training steps" | Flowchart | Forward → loss → backward → update |
| "explain the Krebs cycle" | HTML stepper | Click through stages. Never a ring |
| "draw the database schema" | mermaid.js | erDiagram syntax |
The illustrative route is the default for "how does X work" — don't default to a flowchart because it feels safer.
---
Art and Illustration
For "draw me a sunset" / "create a geometric pattern":
- Fill the canvas — art should feel rich, not sparse
- Bold colors: mix
--color-text-*categories for variety - Art is the one place custom
<style>color blocks are fine — freestyle colors - Layer overlapping opaque shapes for depth
- Organic forms with
<path>curves,<ellipse>,<circle> - Texture via repetition (parallel lines, dots, hatching) not raster effects
- Geometric patterns with
<g transform="rotate()">for radial symmetry
Related skills
FAQ
What is the required fragment order?
style, then HTML content, then script for token streaming.
Which CDNs are allowed?
cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, and esm.sh only.
Where does explanatory text go?
In normal response text outside the show_widget tool call.
Is Generative Ui safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.