Skip to main content
ClaudeWave
Skill1.2k repo starsupdated 2d ago

visualize

Render a polished visual inline in the chat as part of your answer — a diagram, a chart, an interactive explainer, or a UI mockup. Load it proactively whenever an explanation would land better as a picture than as prose. Do not wait to be asked.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/vellum-ai/vellum-assistant /tmp/visualize && cp -r /tmp/visualize/assistant/src/config/bundled-skills/visualize ~/.claude/skills/visualize
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

You are authoring a self-contained HTML fragment that renders inline in the chat transcript, directly beneath the paragraph you are writing. It renders in a sandboxed frame sized to your content, with the host's design tokens injected, so it looks native in light and dark mode.

## Invocation

```
ui_show { surface_type: "visual", data: { html: "<fragment>", height: 320 } }
```

- `height` is a rough pixel estimate (80 to 1400); the host measures and corrects after first paint.
- One visual per call. To change one already shown, `ui_dismiss` its `surface_id` and show a new one.
- Prose goes in your reply, the visual goes in the tool: no titles, intros, or captions inside the fragment, and never describe in prose what the visual already shows.
- If ui_show returns an error, read it, fix the arguments, and call again. Every rejection is fixable in the next call; never debug via shell or files, and never narrate retries.

A visual is part of one answer. Durable things the user reopens (a dashboard, a tracker, a calculator they keep) are apps: use the `app-builder` skill.

## Design instincts

- Seamless and flat: no gradients, shadows, glow, or texture. Flat fills, hairline borders.
- Compact and honest: show the essential, explain the rest in prose; every number on screen is one you actually have.
- Pick the lightest form that carries the idea, and invent freely: a bespoke drawing that fits this answer beats a stock layout. Interaction has to earn its place; steppers are for stages that genuinely follow one another, tabs are not a default. Vary layout between visuals in one conversation.
- Route diagrams on the verb: "walk me through" wants a flowchart; "how is it organised" wants containment boxes; "how does it actually work" wants the mechanism drawn as an intuition, not a safer flowchart.

## Sandbox constraints (hard)

- No network: no script src, stylesheets, @import, remote images, web fonts, fetch, or CDN libraries. Charts and diagrams are hand-drawn inline SVG; images are inline SVG only.
- Fragment only: no DOCTYPE, html, head, or body. No HTML comments, no emoji.
- Hard cap 24000 characters; aim well under 8000. Too big to fit is too big to render. Split rich subjects into two visuals with prose between.
- No position fixed or sticky, no nested scrollbars: the frame auto-sizes from in-flow content.
- No localStorage, sessionStorage, or cookies. Hold state in JS variables.

## Fragment structure

Order: a visually hidden `<h2 class="sr-only">` one-sentence summary, a short style block, the markup, one script element last. Always quote every attribute value (`class="row-box hit"`, never `class=row-box hit`). Include verbatim when using sr-only:

```css
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
```

Usable width is about 660px; the host insets content 6px vertically and 10px from the sides. Rows holding controls carry their own padding of at least 8px on the crowded sides. Keep the outer background transparent.

## Design tokens

The COMPLETE list of injected CSS variables. Any other var() name is rejected unless your own style block declares it. Never hardcode a colour: hex, rgb(), hsl(), and oklch() literals are rejected everywhere, including SVG fill and stroke; only `transparent` and `currentColor` are allowed.

Semantic tokens flip automatically between light and dark. Use them for every surface, body text, border, and status colour:

- Surfaces: --surface-base (page), --surface-lift (card), --surface-overlay, --surface-sunken (recessed tile), --surface-hover, --surface-active.
- Text, strongest to faintest: --content-emphasised, --content-strong, --content-default (body), --content-secondary (labels and any text under 14px), --content-tertiary (14px and up only), --content-quiet, --content-faint, --content-disabled, --content-inset (on inverted fills). Text at 11 to 13px always takes --content-secondary or stronger.
- Borders: --border-base (hairline), --border-subtle, --border-element (visible control edge), --border-hover, --border-active, --border-disabled.
- Status pairs: --system-positive-strong/-weak, --system-negative-strong/-weak (and --system-negative-hover), --system-mid-strong/-weak, --system-info-strong/-weak. A non-text glyph sitting on its own -weak fill takes --system-positive-on-weak or --system-negative-on-weak; those clear the 3:1 that non-text indicators need, which --system-negative-strong misses on --system-negative-weak in the dark theme. Text on a -weak fill needs 4.5:1 and takes --content-default or stronger; --content-tertiary misses that on both fills in the light and dark themes, and --content-secondary misses it on the negative fill in light.
- Type: --font-sans (default), --font-mono (identifiers, code, tabular numbers only), --font-serif (editorial pull-quotes only).
- Radius: --radius-xs 2, --radius-sm 4, --radius-md 8, --radius-lg 12, --radius-xl 16, --radius-xxl 20, --radius-pill 999.

Palette ramps are for categorical encoding only, never page surfaces or body text: --color-moss-50..950 and --color-stone-50..950 (neutrals), --color-forest-100..950, --color-emerald-100..950, --color-danger-100..950, --color-amber-100..950. Stops run light (100) to dark (950). Author every ramp use against the light theme as a matched triple from ONE ramp, and the host mirrors it in dark mode as a unit:

```css
background: var(--color-forest-100);
border-color: var(--color-forest-600);
color: var(--color-forest-900);
```

The two rules the mirror imposes: text on a tinted fill takes the same ramp's 900 (secondary 800), never a --content-* token; and text sitting on the page (SVG labels, axis ticks, anything outside a tinted fill) takes --content-*, never a bare ramp stop. A dark ramp stop used as text is only valid where its light counterpart is painted right there on the same element or enclosing group.

Colour discipline: colour encodes category, not sequence. At most two accent ramps per visual plu