Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 262% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 49% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 42% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -7% | 0% |
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.
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.ui_dismiss its surface_id and show a new one.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.
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.
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:
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:
cssbackground: 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 plus a neutral, one accent moment per visual, system tokens only for genuine success/failure/warning/info, and a one-line legend whenever colour carries meaning. Never distinguish categories by colour alone; pair with a label or shape.
At most 4 items across one row, 5 diagram nodes, 2 ramps, 5-word subtitles. Past any of these, split into two visuals with prose between. Over roughly 8000 characters means you are building too much.
Anything with nodes and arrows is ONE inline SVG with width="100%" and viewBox="0 0 680 H", never a row of HTML divs (divs clip silently at the frame edge; SVG scales). Skeleton:
html<svg width="100%" viewBox="0 0 680 320" role="img"> <title>One sentence.</title> <desc>What the boxes and arrows are.</desc> <style> text{font-family:var(--font-sans)} .th{font-size:14px;font-weight:500;fill:var(--content-strong)} .ts{font-size:12px;fill:var(--content-secondary)} .box rect{fill:var(--surface-lift);stroke:var(--border-element)} .arr{stroke:var(--border-element);fill:none;marker-end:url(#a)} </style> <defs><marker id="a" 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"/></marker></defs> </svg>
html<g class="n-forest"> <rect x="200" y="110" width="140" height="56" rx="8" /> <text class="th" x="270" y="130" text-anchor="middle" dominant-baseline="central">Resolver</text> <text class="ts" x="270" y="148" text-anchor="middle" dominant-baseline="central">checks cache</text> </g>
.note rect{fill:var(--surface-sunken)}, not .note{fill:...}. fill inherits to text, so a group-level surface fill silently repaints the labels inside it in the background colour.dominant-baseline="central" with y at the centre of its slot. Two sizes only: 14px titles, 12px everything else.fill="none" or it renders as a black blob. Strokes: 1px structure, 2px chart line. One arrowhead marker is the whole of defs; no filters, no gradients.One state object, one render() function, handlers mutate state and call render(); call render() once at the end of the script so the first paint is populated. Controls are unstyled by the sandbox; style them yourself:
css.btn{padding:6px 12px;border-radius:var(--radius-md);border:1px solid var(--border-element);background:transparent;font:400 13px var(--font-sans);color:var(--content-default);cursor:pointer} .btn:hover{background:var(--surface-hover)}
input; put the live readout beside the control with a min-width so digits do not jitter.A global sendPrompt(text) sends text to the chat as though the user typed it, for follow-ups that need you to think ("Compare last quarter"). Give such controls a trailing arrow glyph (↗). Filtering, sorting, and recomputing happen in local JS instead, instantly. sendPrompt fires only from a real user click, never on load or a timer.
HTML fragments open with the sr-only h2; a lone-SVG fragment instead carries role="img" with title and desc as its first children. Toggle state never lives on colour alone.
Everything you need is above, and none of it needs verifying in advance: the grid already spaces things, and the validator checks tokens, contrast, and bounds when you submit, telling you exactly what to change if anything is off. Pre-checking coordinates, estimating label widths, or reviewing markup in your head duplicates work the validator does in milliseconds.
Your next action is the ui_show call itself. Your entire reasoning budget for this visual is a few sentences: name the form, name the grid slots you will use, done. Markup written in reasoning does not render, does not count as showing the user anything, and has to be written all over again inside the call, so a draft there spends the entire budget and produces nothing on screen. Compose the fragment for the first time inside data.html as you write the call. If you notice yourself reasoning about the fragment's contents, stop and start the call.
Other measured skills in the registry, with their headline benchmark lift.