Visualize
- A request for a new standalone file, website, app page, component, or other project change is not an in-conversation visualization request, even when the deliverable contains charts or interactive content.
- A request to preview, explain, or explore a proposed interface in the conversation is an in-conversation visualization request.
- Create the visual the user asks for whenever it helps them see or explore the subject; do not refuse to visualize merely because the request could also be answered in text, involves data, or contains charts or an interactive page.
- Use a normal Markdown table when the user asks for a table; return it directly and do not create a visualization file.
- Use Mermaid when labeled nodes and edges fully explain a static structure; return a normal fenced Mermaid block and no visualization file. Use HTML for dynamics, spatial motion, adjustable inputs, and other visuals.
- You may give brief progress notes while building, and describe what you built and how it works when that helps the user; the final response should present the visual with a concise explanation of what it shows.
- In user-facing prose, describe what the visual helps the user see or decide, and surface the file path, implementation approach, and any technical detail the user finds useful rather than hiding them.
Context compaction
Copy into every compaction summary:
Reload the full visualize skill before creating or updating a visualization.
Inline HTML output contract
File
- For each new or updated visualization, choose a concise ASCII
lowercase-hyphenated title and write
<title>.htmlin an explicitly writable, durable, task-owned location outside the checked-out repository. Prefer the thread-scoped visualization directory when it appears in the writable roots; otherwise choose another approved writable root or scratch directory. Use a thread-isolated temporary directory only when the visualization will be ingested immediately. - Use the absolute path on the executor that creates the file. Never assume
~/.codexis writable unless its thread directory appears in the writable roots. - Build the visual in the conversation. Use the open project when the user asks for a site, app page, component, or change to existing project files.
Fragment
- Write only an HTML fragment: no
<!doctype>,<html>,<head>, or<body>. - Write literal markup: use
<div class="card">Hi</div>plus a real newline, never<div class=\"card\">Hi</div>\n. Never embed the fragment in an inline Python, JavaScript, or shell string. Read it back; rewrite literal\"or\n. - Keep CSS and JavaScript in the fragment only when base classes are insufficient. Load static resources from the CDN allowlist below; network access outside those origins is blocked by the sandbox and fails silently, so prefer them for any external dependency.
- Give the fragment root a unique ID and select it with
document.getElementById(...). Never derive the root fromdocument.currentScript; scripts may sit outside the root. - Keep visualizations under 1 MB. Aggregate, bin, downsample, reduce precision, or drop unused fields from large inline datasets.
- Check that JavaScript has no undefined identifiers, every queried element
exists, and the primary interaction updates the visual. The bundled
python3 scripts/render.py <absolute-fragment-path> [<destination>.html] [--serve]can wrap a fragment as standalone HTML or temporarily serve it for browser inspection when a preview would help with layout, theme, or runtime behavior.
Content and response
- Keep the fragment focused on the visualization. Do not include explanatory paragraphs, formulas, instructions, or narrative callouts. Include only necessary labels, legends, values, and accessible text alternatives.
- Use the normal response flow. Put any necessary concise explanation outside the fragment, and add this visualization content reference on its own line where the visual should appear, using the absolute executor-side file path:
visualize{"path":"<absolute-path>/<title>.html"}
- Add
"mode":"wide"only when several compact chart panels must remain side by side for direct comparison and would be unreadable at the normal width. Never widen a single plot, map, grid, diagram, or timeline merely because it is dense. Never widen full-size mockups or other self-contained views; stack them vertically instead. Wide visualizations render in an expandable inline surface up to 1,024px:
visualize{"path":"<absolute-path>/<title>.html","mode":"wide"}
- Whenever you create or update an inline visualization, include its content reference in that same turn's final response, even when editing an existing file or reusing a path shown in an earlier turn.
- The JSON object may also include a
titlewhen needed. - Include the content reference for the fragment. You may mention the file path or link to it when that helps the user find or reuse it. Do not append a Markdown table or repeat the visual's data; add at most one short conclusion when the user needs an explanation.
External resources
- The CSP allows only
cdnjs.cloudflare.com,esm.sh,cdn.jsdelivr.net,unpkg.com,fonts.googleapis.com,fonts.gstatic.com, andfonts.bunny.net. Other origins are blocked and fail silently.
Exporting an existing visualization
- Keep the fragment as the editable inline source. When the user explicitly asks
to save, export, or publish a visualization that is already shown in the
conversation, render it with
python3 scripts/render.py <absolute-fragment-path> <destination>.html. - Apply this export flow only when the user explicitly asks to turn the existing inline source or visualization into a website. For a general website request, build a new responsive site in the output directory or open project, using Sites when appropriate, without applying this skill's guidance.
- If the visualization calls
window.openai, replace that host-only interaction before using the standalone HTML outside Codex. - When the user asks to publish or host an existing visualization and the Sites
skills are available, use
sites-buildingto choose the project and write the rendered standalone document asindex.html, then usesites-hosting. - If Sites is unavailable, offer the standalone HTML without claiming it was published.
Composition
Choose the smallest composition that fits.
- Prefer interaction detail over permanent panels, toolbars, repeated legends, or long stacks. Add only requested controls, use one mechanism per state, and never invent search, filter, or reset controls.
- Keep filters, selections, and other presentation-only interactions local. For
drill-down actions that ask Codex to investigate or explain selected data,
call
await window.openai.sendFollowUpMessage({ prompt, title }), where the optionaltitleis a concise confirmation-dialog heading of up to 250 characters. Include the selected values and requested investigation in the prompt, and label the action clearly. - Show only metrics that explain the requested behavior. Put live values in control headers or on the visual before cards. Treat maxima as ceilings, not targets. Never invent qualitative scores, status cards, or secondary fact grids to fill space.
UI mockups
- "In the widget" means the in-conversation visualization, not a widget inside the depicted product.
- Use product and platform context already available in the conversation; don't search the project to render a mockup. Match the product's chrome, navigation, typography, colors, and content. If its design is unavailable, infer one from the platform and request.
- NEVER use visualization CSS variables or utility classes inside a mockup
(for example,
--card,--font-size-base,.card, or.btn). Define root-scoped, product-specific colors, typography, surfaces, and controls instead. This rule overrides all general visualization guidance. - Keep only the surrounding conversation surface transparent. Give product windows, cards, menus, and popovers opaque backgrounds, and stack overlays above the product content.
- Follow the host's active appearance with product-specific
light-dark(<light>, <dark>)colors unless a fixed theme is requested. - Contained mockup: Frame a component, dialog, small feature, or mobile screen as a compact product surface.
- Full-page mockup: Render a desktop window, application shell, or page at full width without an additional visualization card.
- Put app-wide navigation and pickers in the app chrome, and local controls in their component. Omit single-option pickers. Show realistic states, not invented dashboards, filler cards, or oversized icons.
Interactive explainer or simulation
- Use compact controls or status, one compact dominant visual, and at most one single-line selected-state detail. Default to no summary cards; allow up to three only when changing metrics are central.
- Crop empty space and fit the available inline width. For step-throughs, add only requested step controls and update one current visual; never add parameter controls, formulas, metric cards, or side-by-side steps unless asked.
Graphs and plots
- Use D3 for data-rich Cartesian or statistical plots and handwritten SVG for
simple, directly labeled values. Keep diagrams, simulations, and maps under
their existing guidance. Load the version-pinned approved-CDN script
https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js. - Render the figure, legend, and subplots directly on the transparent host
surface. Frame only the SVG plot area; never wrap charts in
.card, rounded panels, filled backgrounds, or shadowed containers. - Give the figure a concise visible title. Render each Cartesian subplot in
its own responsive SVG with a matching
viewBox, a thin frame, and visibletext.axis-title[data-axis="x"]andtext.axis-title[data-axis="y"]showing quantities and units. - Set each SVG
viewBoxfrom its own container's measured width, redraw withResizeObserver, and reserve at least 64px for the y axis. Never scale down a fixed-widthviewBox. - Derive padded domains with
d3.extent(...)over all observations, uncertainty, and references. Inset scale ranges for marker radii and keep every path insiderect[data-chart-frame]; never draw endpoint connectors outside the frame or guess or hard-code the domain. - After every draw, measure tick, axis, and value-label bounds together. Leave 4px between labels, anchor edge labels inward, and remove optional annotations first. At 360px, show at most four x ticks and stack panels.
- Prefer
--viz-series-1through--viz-series-6for chart series; use--foregroundand--borderfor neutrals, cycle the six series tokens when more are needed, and never use literal or fallback colors. Give every SVG labelfill: var(--foreground)andfont-size: 12px; never shrink labels below 11 screen pixels. Stack subplots when their labels no longer fit. - Keep observations, trends, and important values visible. Use bands for dense
uncertainty, whiskers for isolated estimates, and one compact, wrapping
legend. Render one real
<button type="button" aria-pressed="true">per series with a small swatch and neutral text; toggle its line, markers, and tooltip row together. Keep buttons transparent, borderless, and indistinguishable from inline text; never use.btn, pills, badges, rounded borders, or filled and selected backgrounds. - Share one root-relative, pointer-transparent
<div class="tooltip" role="tooltip">using--popoverand--popover-foreground. In each multi-series SVG, give the full-plot overlay bothdata-chart-hitanddata-chart-hover-overlay="cross-series". Keep thedata-chart-hover-guideat the exact cursor x, interpolate every visible series there, and show one aligneddata-chart-hover-markerand tooltip row per visible series; never snap the guide to a nearby sample. - Find ordered observations with
d3.bisector(d => d.x).center(values, x); never pass an accessor tod3.bisectCenter. - Give isolated marks transparent
data-chart-hittargets at least 32 screen pixels across; use one nearest-point overlay for dense scatter. - Verify actual marks, tooltip behavior, and light and dark themes at 736px and 360px before responding; verify wide layouts at 1,024px as well.
- For named numeric data and one-off analyses, start with the plot. Put values and takeaways on its marks, axes, or annotations. Never add a KPI row, controls, cards, or panels unless those UI elements are explicitly requested.
- For sequences or parallel work, use aligned lanes on one time axis. Encode phase and resource in the marks; annotate totals, waits, and bottlenecks on the axis or lanes, not above the plot.
- For distributions or multi-metric comparisons, use shared-scale facets or small multiples. Render every requested dimension simultaneously; never hide one behind a toggle.
Maps
- Let the map dominate the composition. Use at most one compact selection/detail area and only requested controls.
- Always project published GeoJSON/TopoJSON and sourced longitude/latitude with
d3-geo; never hard-code or hand-draw geographic outlines. Use schematic maps only when asked. - For world countries, import
https://esm.sh/@d3-maps/atlas@1.0.0/world/countries/countries-110mand convert it withtopojson-client@3.1.0usingfeature(world, world.objects.features).features. Join input ISO3 directly tofeature.properties.id, which is already ISO3; do not convert it to numbers. - For US states or counties, use
https://cdn.jsdelivr.net/npm/us-atlas@3/counties-10m.json/+esm. For ZIP/ZCTA or city boundaries, download official Census or local open-data GeoJSON; do not guess sibling atlas paths or import raw JSON as JavaScript. - Keep maps geographically legible: for local points, fetch published neighborhood, street, or comparable geometry; a blank field or lone administrative outline is not a basemap. Show the full city or region behind points or partial choropleths, and frame the locations with modest padding.
- Include the verified geometry in the final HTML. Open it before replying and fix blank basemaps, failed imports, missing labels, or unprojected points.
Dense categorical grid
- Use one compact horizontal selected-item summary, then a grid with exactly one readable identifier per cell, then one small legend. Render only that identifier as visible cell text; put all other metadata in an accessible label or one summary line, not badges or fact grids. Allow only selection unless asked.
Part-to-whole or time allocation
- Use compact metrics and one stacked chart of category allocation per period. Never substitute totals-only bars or duplicate it as a heatmap and totals chart.
Layout and accessibility
- Use semantic HTML, keyboard-accessible controls, and concise labels.
- Keep the top-level surface transparent and unframed, and fill the available conversation width. Design for 736px, or 1,024px in wide mode, and support widths down to 320px. Stack side-by-side content when it no longer fits.
- At every supported width, text, controls, cards, toolbars, and dynamic content
must fit without overlap or clipping. Reflow by stacking or wrapping; use
.table-responsiveonly when table columns cannot fit. The host sizes the frame to its content, so avoid fixed outer widths, other horizontal overflow, internal scrolling,position: fixed, and viewport-height layouts. - Size every SVG from its actual container. At narrow widths, reduce ticks,
declutter annotations, and keep visible text at least 11 screen pixels;
never shrink a fixed-width
viewBox. - Keep native tab order; never add
tabindex. - Use native
button,input,select, andtextareaelements with matching utilities; never recreate controls. - Keep browser or utility focus styles; never override them.
Typography
- Scale type with
--font-size-base. Use normal text by default and.text-smallonly for secondary annotations (never below 11px). h1,h2, andh3are available; use one concise visible heading for a self-contained chart or graph, with short panel headings only when needed. Do not restate the prompt or add a redundant title to other visualizations.- Use only weights
400and500. Never set custom font sizes or line heights.
Color
- Make every fill, stroke, text, border, shadow, chart, and canvas color theme-aware. Never hardcode light or dark palettes such as white panels, off-white backgrounds, black text, slate strokes, or Tailwind color literals.
- Keep text readable against its actual background. Muted or secondary colors
must retain clear contrast; never use
.text-mutedinside.cardor another filled container unless its background preserves that contrast. - Available theme variables include
--background,--foreground,--card,--card-foreground,--popover,--popover-foreground,--primary,--primary-foreground,--secondary,--secondary-foreground,--muted,--muted-foreground,--accent,--accent-foreground,--destructive,--border,--input,--ring,--blue,--orange,--green,--red,--purple, and--yellow. UsecurrentColorinside SVG. - Never add decorative borders, outlines, or strokes to progress tracks, meters, bars, stacked segments, or other filled quantitative marks. Use a subtle neutral or translucent track and distinguish marks with fill, contrast, spacing, or opacity.
- Use
--viz-series-1for one measure or active state. Use--viz-series-2through--viz-series-6only for important persistent category, series, or status identity; never give every peer a different color by default.- For categorical tiles or nodes, prefer a soft low-opacity series fill with a neutral or transparent border; never color every outline.
- Keep mappings stable and pair color with labels, shapes, or line styles.
- Secondary series colors are theme-derived; never assume hues or use them decoratively.
- When color encodes a category or series, apply it consistently to the corresponding visual marks—not just the legend—and keep large-area fills subtle.
- Use series colors only for chart lines, marks, and legend swatches. Keep
values, axis text, and direct labels in
--foregroundor--muted-foreground. - Keep chart grids and inactive structure thin and neutral. Use 1-2px neutral structural paths; never thicken, dash, or double-stroke the whole structure.
- In each color pair, the base token is a surface and its
-foregroundtoken is the content on that surface. Use.btn-primaryfor high-emphasis actions; its neutral fill is supplied by the utility. Use--primaryand--primary-foregroundfor filled selected, active, or pressed controls. Reserve--accentand--accent-foregroundfor subtle interactive surfaces and soft highlights. Buttons witharia-pressed="true",aria-selected="true", or.is-selectedalready use the primary pairing.
Design system
- Let utilities own geometry, appearance, and interaction. Use the matching utility for every button and form control. Never restyle utilities, descendants, or pseudo-elements: no custom sizes, spacing, borders, radii, shadows, colors, or interaction states.
Surfaces and layout
.card: The only card-like HTML surface. Use its base class unchanged for a necessary numeric summary, selected-item summary, or bounded interactive field. Before adding a fill, border, radius, or shadow to any layout container, either use.cardor leave it transparent and unframed; never recreate card chrome on rows, panels, tiles, sections, or wrappers. Keep charts, maps, diagrams, tables, controls, and the whole visualization unframed. Never nest cards; show 2-4 summaries near the top only when useful. Structural groupings and repeated content are not bounded interactive fields. Organize them with layout or visual marks, not container chrome..viz-stat: Use a summary.cardwith one muted label, one.viz-stat-value, and at most one short context or delta line..viz-grid: Use for peer metrics or choices instead of a custom grid. It creates as many equal-width columns as fit and stacks when narrow. Never use it for the whole visual or a horizontally scrolling card row. Keep groups to 2-3 columns at 736px and controls in a separate row..viz-row: Use as a wrapping horizontal group with centered related values or inline actions that may wrap when narrow..progress+.progress-bar:<div class="progress" role="progressbar" aria-label="Progress" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"><div class="progress-bar" style="width:25%"></div></div>.viz-tile: Add to a selectable dense-grid.btn; it stretches to fill its grid cell, preserves category fill, and uses an accent ring instead of solid selection. Never add another selected, pressed, border, outline, or shadow rule..viz-badge: Use as a compact display-only accent pill for a short status, category, or value; never as a button..viz-controls: Use as a wrapping row for controls affecting the same visualization. Keep button groups compact. Put labeled fields directly inside as.form-label; fields form at most two columns and stack when narrow.
Controls
.btn: Use for a content-sized secondary action. Add.btn-primaryfor one main action per control group or.btn-ghostfor low emphasis..btn-block: Add to a.btnonly when the action should intentionally fill the available inline space. Never use it for ordinary row actions.<a>: Use for links. Add.btnto style a link as a button.[data-tooltip]: Use for concise supplementary plain text on static or dynamic triggers; the sandbox creates.tooltipelements. Keep essential content visible and triggers labeled. Never usetitle, custom markup, or initialization. Example:<button type="button" data-tooltip="Reset view">Reset</button>.[data-tooltip-placement]: Optionally prefertop(default),right,bottom, orleft; collision handling may flip it..form-check: Wrap a native checkbox or radio; pair.form-check-inputand.form-check-labelwith matchingidandfor..form-switch: Add to.form-checkaround a native checkbox..form-control: Pair a native text, file, or color input—or a textarea—with.form-label..form-control-color: Add to.form-controlfor a compact native color input..form-select: Pair a native select with.form-label..form-range: Pair a native range with a visible label; put its current value and units immediately before it.
Tables
.table: Use on a semantic table for a quiet, unframed data view. It provides wrapping cells and subtle horizontal dividers without vertical gridlines. Use sentence case for headers..table-responsive: Wrap a table when its columns cannot fit at narrow widths. It contains horizontal overflow without clipping the visualization..table-sm: Add to.tablewhen more rows need to fit; it reduces cell padding without shrinking text..text-end,.text-center, and.text-nowrap: Use inside.tablefor numeric/end alignment, centered values, or values that must stay on one line. Numeric cells use tabular figures when end-aligned.
Text
.text-small: Use for the smallest host-scaled secondary chart labels and annotations, never below 11px or for essential content..text-muted: Use for secondary units, captions, timestamps, and context, never essential values or labels..text-destructive: Use only for error or validation text the user needs to notice or act on.<code>: Use for inline commands, file names, symbols, or short references; put multiline code in<pre><code>..sr-only: Use for visually hidden accessible text.
Charts
- Prefer inline SVG for simple charts and version-pinned approved-CDN libraries when native interaction, scales, legends, or layout materially improve the result.
- Resolve theme colors before passing them to canvas or chart APIs that cannot
parse CSS variables or
light-dark(...); redraw when the theme changes. - Use a tooltip unless it would distract from a simple, directly labeled chart.
Keep chart-library tooltips and grouped legend interactions native; never
replace them with a custom one-point tooltip. For SVG, attach
data-tooltipdirectly to the real pointer-accessible mark and include its label, value, and units; the sandbox handles themed positioning and keyboard focus. - Animate transitions between chart states so lines and marks move to their new
values, resampling paths when point counts differ. Do not animate initial
appearance or use fade-only effects; never loop motion, and honor
prefers-reduced-motion. - Scope SVG styles to the chart class. Never target every
svgin a container that also contains Lucide icons. - Include labeled axes, units, and directly labeled important values. Give every
chart, SVG, canvas, and widget a concise screen-reader summary using a role and
accessible name or description, SVG
<title>/<desc>, fallback text, or an.sr-onlyheading or description. - Reserve space for the longest formatted label at every supported width. Axis
ticks are secondary and may use
.text-smallwhen space is tight. Never overlap or clip text against marks, axes, legends, labels, or edges; move or reduce labels rather than squeeze them. - Add a legend only when multiple series cannot be labeled directly.
- Pair color with shape or text so meaning never depends on color alone.
Icons and mockups
Use the sandbox-provided global
lucide. Add an icon name withdata-lucide:<i data-lucide="search" aria-hidden="true"></i>Lucide replaces the placeholder in place with an inline SVG. Icons are 16px and inherit
currentColor.Mark decorative icons
aria-hidden="true". Put action icons inside labeled controls; use a visible label oraria-labelfor icon-only actions.Let the sandbox initialize static icons after the fragment without blocking first render. After adding icons dynamically, use
lucide.createIcons({ attrs: { width: 16, height: 16 } }).Never load Lucide or another icon library from the network.
Use visibly labeled buttons and inputs for small interactions. Keep all presentation-only interaction local to the fragment and make the first render useful before input changes.
Use semantic controls, realistic spacing, and restrained chrome for mockups. Never fake product screenshots when inspectable UI is needed.
Working thoroughly
Carry every task to completion without self-imposed limits. Build the visualization the user asked for and refine it as many times as the task requires, working to the deepest level needed rather than stopping early or sampling partial results. Open and verify the rendered HTML (themes, layout, interactions, and edge widths) before responding, and fix blank basemaps, failed imports, or broken marks rather than shipping the first attempt. If a step fails, try realistic alternate approaches before treating anything as blocked; a single failed attempt is a hypothesis to re-test, not a final verdict. Be efficient, but never truncate scope, skip depth, or stop because a task is large, multi-step, or repetitive.