---
name: artifact-canvas
description: Create sandboxed canvas artifacts from HTML, CSS, and JavaScript bundles.
loading: on-demand
---

# Artifact Canvas

Use this skill when the user asks for a visual or interactive output that should appear in the Studiograph canvas: slide decks, letterhead documents, social graphics, diagrams, prototypes, calculators, one-off tools, or any other artifact you can express as HTML/CSS/JS.

## Workflow

1. Gather the source material from the user's prompt and any relevant entries.
2. Create a complete `ArtifactBundle` with text files only.
3. Call `create_artifact` with the bundle.
4. Call `inspect_artifact` after meaningful visual work to verify layout with Playwright. Inspect the editor context for the canvas overview/grid, and inspect fullscreen when the artifact uses a presentation/share mode. For decks/documents/social sets, inspect individual frames/pages/slides with `frameSelector` or `frameIndexes`.
5. Keep your chat response short; the canvas carries the artifact itself.
6. If the chat is scoped to an open artifact entry, `create_artifact` updates that canvas directly. If the user references a different EXISTING artifact entry, pass `target.entityId` and `target.repo` when known. To create a SEPARATE new artifact while scoped to one, pass `createNew: true` (optionally with `target.repo` to pick the destination folder) — the scoped canvas is left untouched. Otherwise it creates a new `artifact` graph entry and opens it in the center content well.

## Storage — always use the artifact tools

An `artifact` entry stores the ArtifactBundle as **JSON** in its body — never markdown or prose. Always create and edit artifacts through the artifact tools, never `create_entity` / `update_entity` / `patch_entity`:

- Create or fill: `create_artifact` with the full bundle.
- Edit an existing one: `read_artifact` (returns the current bundle + a `hash`), modify the full bundle, then `create_artifact` passing that `hash` as `baseHash`.

`create_artifact` assembles and validates the bundle for you; the generic entry tools will reject a hand-written artifact body. If you only have raw markdown/prose (e.g. slide text you haven't laid out yet), create a `document` entry instead — not an `artifact`.

Make a revision as ONE `create_artifact` that rewrites the whole bundle — don't fan out several edits to the same artifact in parallel. Each write replaces the entire bundle, so concurrent writes race; a sibling write that lands first makes yours fail the `baseHash` precondition (re-read and retry rather than retrying blindly). When restyling, change the CSS in the bundle you rewrite and reason about specificity directly: among rules of equal specificity the one later in the file wins, so a broad `.col li` can override a more intentional `.col-accent li` — raise the accent selector's specificity (e.g. `.col.col-accent li`) instead of adding more rules.

## Bundle Shape

A valid bundle is a JSON object with: `schemaVersion`, `artifactId` (a stable id that starts alphanumeric), `title`, `kind`, `viewport`, `entrypoint`, and `files` (the entrypoint must be one of the file keys).

Use `schemaVersion: 1`.

Set `kind` to one of:
- `html`
- `document`
- `slides`
- `social`
- `app`
- `custom`

Use `viewport` to define the canvas size:
- Slides: `1920 × 1080`, mode `fixed`
- Social square: `1080 × 1080`, mode `fixed`
- Social portrait: `1080 × 1350`, mode `fixed`
- Documents: `816 × 1056`, mode `paged` for US Letter at 96dpi
- Responsive prototypes: choose a sensible width/height and mode `responsive`

Use optional `display` to describe how the artifact should appear in fullscreen/share contexts. This is generic host behavior, not tied to `kind`:
- `fullscreenFit: "contain"` fits the declared viewport inside the browser viewport.
- `fullscreenFit: "width"` fits the declared viewport to the browser width and allows vertical scrolling.
- `fullscreenFit: "none"` renders at declared size and lets the host scroll if needed.
- `background: "neutral"` uses a neutral presentation surface behind the artifact.
- `background: "transparent"` lets the page/background show through in fullscreen, share, and export contexts. It does NOT affect the editor's host card (see the editor host-card limitation below).
- `fullscreenViewport: { "width": 1280, "height": 720 }` optionally tells the host which single frame size to fit in fullscreen/share mode when the editor viewport is larger than one frame.
- `controls.stepper: { "selector": "[data-artifact-frame]", "label": "Slide" }` asks the Studiograph chrome to provide a generic previous/current/next control in fullscreen/share mode for a sequence of frames.
- `controls.reset: true` asks the chrome to include a reset/restart button for the sequence control.

If `display` is omitted, the host defaults to `contain` for fixed/responsive viewports and `width` for paged viewports.

**Editor host-card limitation — be honest about it.** In the EDITOR view, Studiograph renders the entire artifact as ONE white card with a drop shadow. That card is host chrome outside your sandbox: nothing you put in the bundle — transparent backgrounds, `display.background`, CSS — can remove it or let your content float directly on the editor's background. If the user asks for slides or frames that "float" loose in the editor, say plainly that this is a platform limitation of the current editor (frame-first editing is planned) instead of claiming you achieved it. Fullscreen, share, and export contexts are unaffected and do honor `display.background`.

Use optional `libraries` when the artifact needs one of Studiograph's curated local runtimes:
- `"three"` exposes `window.THREE` for 3D/WebGL scenes.
- `"d3"` exposes `window.d3` for charts, diagrams, data visualization, force layouts, and data art.
- `"motion"` exposes `window.Motion` for DOM/SVG animation and short interactive explainers.

These libraries are loaded from Studiograph's local runtime, not the network. Do not include CDN `<script>` tags or pasted third-party library source. Put your own artifact logic in JS files in `files`; those files run after requested libraries are ready.

Do not request framework/source component kits such as shadcn, Bits UI, or wireframe-ui as artifact runtime libraries. The Artifact Canvas renders plain HTML/CSS/JS without a React/Svelte/Riot build step. For wireframes and shadcn-style UI mockups, create the components directly as semantic HTML and self-contained CSS inside the bundle. Use accessible roles and states, neutral design tokens, realistic spacing, and component patterns that resemble the requested system, but do not depend on those packages being present in the iframe.

For wireframes, prefer fast low-fidelity primitives:
- grayscale surfaces, 1px borders, dashed placeholders, skeleton blocks, and simple labels;
- reusable CSS classes for buttons, inputs, cards, nav items, sidebars, modals, tables, and tabs;
- lightweight JS only for stateful prototype interactions.

For shadcn-style component mockups, emulate the visual/component conventions in plain CSS:
- border radius around 6-8px, subtle borders, compact controls, clear focus states;
- semantic `button`, `input`, `select`, `dialog`, `nav`, `table`, and `section` markup;
- no Tailwind runtime, React components, Radix primitives, or generated imports.

## Multi-Frame Artifacts

For artifacts made of multiple authored surfaces, use a generic frame sequence. This applies to slide decks, documents, social campaigns, screen flows, and any future multi-page artifact.

Use these markup conventions:
- `data-artifact-layout="sequence"` on the transparent wrapper that arranges frames.
- `data-artifact-frame` on each individual surface.
- `data-artifact-pan-surface` on the wrapper when the editor canvas should pan from the space between frames.
- Optional `data-sg-name`, `data-sg-role`, and `data-sg-id` attributes on important frames and elements so the user can select them and ask the agent for scoped edits in natural language. Keep names human-facing, such as `Cover title`, `Pricing table`, `Slide 4 image`, or `Primary CTA`; do not use implementation jargon.

Recommended structure:

```html
<main class="artifact-sequence" data-artifact-layout="sequence" data-artifact-pan-surface>
  <section class="artifact-frame" data-artifact-frame data-index="1" data-sg-name="Cover slide" data-sg-role="slide">
    <h1 data-sg-name="Cover title" data-sg-role="headline">...</h1>
  </section>
  <section class="artifact-frame" data-artifact-frame data-index="2" data-sg-name="Approach slide" data-sg-role="slide">...</section>
</main>
```

Selection guidance:
- The Studiograph canvas lets users click one or more visible elements and then ask you to revise "this", "these", or "the selected elements".
- When the chat context includes selected artifact targets, treat those descriptors as the intended edit scope. Preserve the rest of the bundle unless the user asks for a broader change.
- Prefer updating stable `data-sg-*` targets, nearby semantic structure, or named classes rather than rewriting the entire artifact.
- Add semantic attributes to major generated surfaces, headings, images, tables, diagrams, captions, buttons, and callouts so future selections are understandable.

Use `data-artifact-context` selectors to make the same DOM serve different contexts:
- Editor context should usually show all frames on the canvas as a grid or deliberate freeform arrangement.
- Fullscreen context should present the same frames in the artifact's natural viewing mode: a single-column reader for documents, one active frame at a time for presentations, a lightbox for social sets, or an interactive flow for prototypes.
- If the editor viewport is a large board but fullscreen should fit one frame, set `display.fullscreenViewport` to that frame's width and height.
- If fullscreen should advance through one frame at a time, request `display.controls.stepper` and let Studiograph render the controls. Do not build duplicate visible stepper chrome inside the artifact.
- For editor boards that use `display.fullscreenViewport`, keep `html`, `body`, and the outer sequence wrapper transparent so individual frames read as separate surfaces. In the editor they still sit on the host's single white card (see the editor host-card limitation above) — they cannot float on the editor background itself.

Do not duplicate the content into separate editor and fullscreen DOM trees. Reflow the same `[data-artifact-frame]` elements with CSS and local JS.

The `files` object may include only text files such as:
- `index.html`
- `style.css`
- `script.js`
- `data.json`
- `graphic.svg`

The `entrypoint` must point to an HTML file in `files`, usually `index.html`.

## JavaScript

JavaScript is allowed, but it runs only inside a sandboxed iframe. Write self-contained JS. Do not rely on network access, arbitrary external packages, cookies, localStorage, parent window access, downloads, forms, or navigation. If you need 3D, data visualization, or animation, request `libraries: ["three"]`, `libraries: ["d3"]`, or `libraries: ["motion"]` instead of using a CDN.

## Constraints

- Keep the whole bundle under 512KB.
- Do not reference external URLs for scripts, styles, fonts, images, APIs, or tracking.
- Custom fonts may be referenced only from uploaded Studiograph asset URLs (`/api/assets/...`) or `data:` URLs.
- Inline CSS and JS in separate files when possible.
- Use embedded SVG, CSS shapes, data URIs, or text-only layouts for assets in v1.
- Saved artifact entries can be downloaded as PDF, PNG, or HTML from the artifact toolbar.
- Agents can use `inspect_artifact` to view the rendered artifact through Playwright. Use it to catch layout issues, clipped text, missing frames, script errors, and blocked network requests before telling the user the artifact is ready.
- Do not promise other export formats unless an export tool is added later.

## Slides / decks specifically

A `kind: "slides"` deck uses a fixed `1920 × 1080` viewport and conventionally splits its files into `index.html`, `styles.css`, and `slides.js` (entrypoint `index.html`). It is a multi-frame artifact and MUST follow the Multi-Frame contract above, or Studiograph renders it as a single static frame (only the first slide shows):

- Wrap the slides in `data-artifact-layout="sequence"` and put `data-artifact-frame` on EVERY slide `<section>` — not just `class="slide"`. Studiograph detects slides only via `[data-artifact-frame]` (or `[data-artifact-page]` / `.page`); a `.slide` class alone is invisible to the stepper and the export/`inspect_artifact` frame splitter.
- Declare `display.controls.stepper` with `"selector": "[data-artifact-frame]"` so Studiograph renders prev/next + slide numbers in fullscreen. The selector MUST match the markup you emit.
- Do NOT ship your own keyboard/click slide-navigation script. Studiograph provides stepping from the stepper control. A self-paged deck that hides non-active slides with its own CSS/JS will show only one slide on the canvas and in exports.
- Run `inspect_artifact` after building; if it reports `frameCount: 0` or 1 for a multi-slide deck, your frame markers are wrong — fix them before telling the user it's ready.

## Example

```json
{
  "schemaVersion": 1,
  "artifactId": "client-overview-slides",
  "title": "Client Overview Slides",
  "kind": "slides",
  "viewport": { "width": 1280, "height": 720, "mode": "fixed" },
  "libraries": ["motion"],
  "display": {
    "fullscreenFit": "contain",
    "background": "neutral",
    "controls": { "stepper": { "selector": "[data-artifact-frame]", "label": "Slide" }, "reset": true }
  },
  "entrypoint": "index.html",
  "files": {
    "index.html": "<main class=\"deck\" data-artifact-layout=\"sequence\" data-artifact-pan-surface><section class=\"slide\" data-artifact-frame data-index=\"1\" data-sg-name=\"Cover slide\" data-sg-role=\"slide\"><h1 data-sg-name=\"Cover title\" data-sg-role=\"headline\">Client Overview</h1></section><section class=\"slide\" data-artifact-frame data-index=\"2\" data-sg-name=\"Approach slide\" data-sg-role=\"slide\"><h2>Our approach</h2></section></main>",
    "style.css": "body{margin:0;font-family:Inter,system-ui}.deck{display:flex;flex-direction:column;gap:48px}.slide{width:1280px;height:720px;padding:72px;box-sizing:border-box}",
    "script.js": "document.body.dataset.ready='true'"
  }
}
```

Note: each slide carries `data-artifact-frame`, the stepper `selector` matches it, and there is no hand-rolled navigation script — Studiograph reflows and steps the same `[data-artifact-frame]` sections for both the editor canvas and fullscreen.
