In Version the logic, not the deliverable, I argued that asking a model to hand-code a deliverable from scratch on every run is a trap. When the model writes raw layout code (like the generic pptx baseline in my benchmark), variance explodes, brand tokens drift, and every visual fix requires another round of agentic QA. When you version the logic — separating the what (structured data and narrative shapes) from the how (a deterministic theme engine and layout bricks) — even a smaller, faster model hits the mark consistently.
Looking under the hood of Claude's latest Artifact architecture (internally called appifacts across its skill files) reveals that Anthropic has arrived at the exact same architectural split — and taken it to industrial scale.
By inspecting the verbatim Artifact tool definition, the artifact-design skill, and the 67 internal SKILL.md and reference/*.md files across the platform's six core Artifact types (Dashboard, Slides, Design, Design System, Docs, and Motion), read in two places (a Claude Code cloud session, and a claude.ai chat on a second account), we can see two distinct regimes operating side by side:
- Tier 1: The "Plain Page" contract (
artifact-design) — where the model hand-authors a raw.htmlfile inside a heavily restricted sandbox. - Tier 2: Typed Artifacts (
type_url) — where every instance runs on a shared, versioned viewer (contracts from0.2.9to0.2.67, served from/_f/<version>/), gets a domain SDK (dash+ D3 in Dashboard) or custom web components (.dc.htmlin Design), binds to first-classDesign Systemartifacts, and replaces slow vision-QA sub-agents with closed grammars and, in Dashboard, DOM telemetry.
Here is the complete anatomy of how Anthropic's Artifact engine works from the inside — where its engineering is brilliant, and where its product strategy crosses into vendor lock-in.
1. The Router & the Three Storage Models
Prompt context is expensive. If Anthropic injected the full specification for dashboards, slide decks, design canvases, design systems, and collaborative docs into every conversation, the system prompt would bloat by well over 100,000 tokens before the user even asked a question.
Instead, in Claude Code, when a user asks for a new piece of work, the Artifact tool mandates a read-only bootstrap call first: action: "quickstart" with an intent ('document', 'slides', 'design', or 'other'). That single call acts as a just-in-time router: it lists the account's published Artifact types, attaches any default Design System, and — if no specialized type fits — carries the page-design guidance itself: the tool definition states that "a quickstart result with the page-design guidance counts as loading artifact-design", so the model doesn't waste a second tool call loading it.
The router is not the same everywhere. The claude.ai chat I inspected on my second account has no quickstart action at all: there, the model lists the types with action: "list", scope: "types" and reads one before recommending it. That chat's system prompt adds a dedicated rule, <make_short_films_from_a_type>: an animation, explainer or short video must be made "from the type Artifact lists for animation and video, whatever its title, not as a hand-written HTML page or a deck". It never names the type; the listing does.
When a listed type does fit, the model is instructed not to write files right away. Instead, it performs an empty bootstrap publish:
To start a new Artifact from one, publish with its
type_url, atitleand no files first (passingauto_open: "after_first_write"when your next step publishes files to it or writes its store)... the result carries the new Artifact'surland the type's instructions, and says how to fill it.
Reading the SKILL.md of each core type reveals that "Artifacts" are no longer single HTML files. Depending on the type, an Artifact instance is backed by one of three completely different storage engines:
| Core Type | Release / Contract | Storage Engine | How Claude Authors Content |
|---|---|---|---|
| Dashboard | 1793998911-b161 (0.2.67) |
Artifact DB Store (ArtifactData / write_db) |
Writes JSON documents to dash/meta, datasets/<id>, and HTML/JS to files/index.html (capped at 256 KB/doc). |
| Slides | 1794002216-9cf2 (0.2.47) |
Virtual File Tree (project/*) |
Publishes project/deck.json + one <section> file per slide at project/slides/<id>.html (3 slides per turn). |
| Design | 1794002220-035f (0.2.47) |
Virtual File Tree (project/*) |
Publishes project/canvas.json + one .dc.html file per artboard (written and published one by one). |
| Design System | 1793927639-4218 (0.2.47) |
Virtual File Tree (project/* + /_blob/) |
Publishes project/design-system.json, tokens.json, README.md, components/bundle.js, and <Comp>/preview.html. |
Docs (pages) |
1793994013-9a2b |
Zero-Disk Cloud Service (Claude Docs) |
Owns nothing on disk. Read and written exclusively through the proprietary Claude Docs connector (create, batch, update). |
| Motion | 1794005674-4689 (0.2.9) |
Artifact DB Store (film + modules + assets) |
"A film owns no files." One batch writes film/meta, modules/scenes.json, modules/base.css and film/script (window.CUT = kit.scenes();); then modules/shared.js, then one modules/scene-<id>.js document per scene, one write per message (200 KiB each, 48 modules max). Images and clips are assets/<file> records pointing to /_blob/ uploads. |
(Note: types are set per account. Motion is listed on only one of my two accounts, so its files were read there. Dashboard also hides a video-like player of its own, called Visual short.)
2. Tier 1: The "Plain Page" Contract (artifact-design)
When none of the specialized types apply, Claude authors a plain .html page governed by artifact-design-skill.md.
Anyone who has built an iframe previewer for LLM-generated code knows the core failure mode: models reach for standard browser APIs that silently fail inside a sandboxed frame. Anthropic's Page contract is a masterclass in defensive prompt engineering, systematically closing every blind spot upfront:
- No structural tags. The host wraps the page in a
<!doctype html>…<head>…</head><body>skeleton at publish time withviewport-fit=coverand[hidden]{display:none!important}. Claude must start directly with<title>and<style>, and toggle visibility withel.hiddenrather thanstyle.display. - Phone safe-area math. Every page must work at
400pxwidth with zero horizontal scroll. Side gutters must usemax(16px, env(safe-area-inset-left, 0px))paired with<meta name="claude:viewport-fit-sides" content="cover">, and sticky headers must paint a fixedbody::beforeband overenv(safe-area-inset-top, 0px). - Strict CSP allowlist & a two-week supply-chain buffer. External scripts can load only from
cdnjs.cloudflare.com(preferred),cdn.jsdelivr.net/npm/,unpkg.com,cdn.tailwindcss.com, orcode.jquery.com, and stylesheets only fromfonts.googleapis.com. Modern ES module bundlers likeesm.share silently blocked. Crucially, every library URL must use a UMD build pinned to an exact version at least two weeks old — never@latestor a major range (react@18). That single rule neutralizes both breaking upstream releases and newly published NPM supply-chain attacks. - Dead APIs mapped upfront.
window.print(),alert(),confirm(),prompt(), and<a download>(includingblob:saves) do nothing in the viewer's frame. The contract forbids "Print / Save as PDF" buttons, requires inline confirmation UI, mandatestry/catcharoundlocalStorage(which throws in previews and thumbnail capture), and restricts URL routing to bare#namehashes (reserving host tokens likesettings,sidebar,customize,upgrade,downloads, andcomment-*). - Hot-reloading state across republishes. If a user is mid-game or mid-form when Claude republishes a page, the viewer preserves state via
window.claude?.hot?.snapshot(...)andwindow.claude?.hot?.ready(start).
A hilarious meta-detail: Anthropic banned its own favorite aesthetic
In my first post, I asked Claude to invent a brand and a design.md from scratch for our benchmark. Left to its own taste, Claude invented "The Signal Brief": warm cream paper (#F7F4EF), a high-contrast display serif (Fraunces), and a single terracotta accent (#C24A22).
Look at what Anthropic's own design leads wrote inside artifact-design-skill.md and design/SKILL.md:
Avoid AI-generated design. AI-generated design currently clusters around a few looks: warm cream (#F4F1EA) with a serif display and terracotta accent; near-black with a lone acid-green or vermilion pop; broadsheet hairline rules with dense columns... Cream, serif display, clay (your habit): only if brief or brand asks.
Claude's attractor state is so overwhelmingly biased toward "warm paper + Fraunces serif + terracotta/clay" (which, not coincidentally, is a close cousin of Anthropic's own Ivory #FAF9F5 + Serif + Clay #D97757 brand palette documented in brand-colors.md) that Anthropic had to call it out by name, "your habit", in the Design type's own instructions!
3. Tier 2: How Typed Artifacts Version the Logic
When Claude steps into a typed Artifact (Dashboard, Slides, Design, Design System), it stops writing raw boilerplate and starts operating as a data-and-structure compiler feeding a pre-engineered runtime.
Slides: The direct counterpart to my carousel benchmark
In my benchmark, the generic pptx skill was slow (up to 853 seconds for Sonnet) and high-variance because the model had to write imperative PptxGenJS positioning code and spawn up to 4 visual-QA sub-agents to inspect screenshots.
Anthropic's Slides Artifact type (slides/SKILL.md + format.md + craft.md) solves the exact same problem using the exact same principles as my carousel skill:
- A closed, declarative CSS grammar. Each slide is a
<section id="…" style="…">on a fixed1920×1080canvas (128pxmargins leaving1664×824inside). There are no classes, no<style>blocks, nomargin, noz-index, noem/rem, and novar(). Every property is validated against a strict grammar table; anything outside it is stripped or held read-only. - Explicit vertical space math & anti-"half-empty slide" rules. Just like the "title floating over a void" defect I caught in my carousel iteration,
slides/artifact-type/reference/craft.mdcodifies the vertical budget in pixels:Inside the 128px margins, you have 824px of height... A two-line heading at 96px with line-height 1.1 ≈ 210px. A gap: 48px. Each table row ≈ 2.1 × the font-size... A CONTENT slide whose column stops at 60% of the height reads as unfinished. Spread out the column on purpose (
justify-content:space-betweenor aflex:1spacer). - A two-pass coordinate recipe for diagrams. Because LLMs are terrible at mental geometry when drawing flowcharts or brackets in a single pass,
diagrams.mdforces a two-pass build: Pass 1 writes only the node boxes plus an HTML comment listing their bounding boxes (<!-- A 0,0,320,120 B 448,0,320,120 -->); Pass 2 reads that comment to attach<x-connector>lines to exact edge midpoints. - The
NEVER VERIFYmandate. Why did Sonnet take 14 minutes in mypptxbenchmark? Because it kept rendering and inspecting pages in a loop. Look atslides/artifact-type/reference/deck-files.md(the same rule closes step 5 ofslides/SKILL.md, step 3 ofdashboard/SKILL.mdand step 6 ofdesign/SKILL.md):NEVER VERIFY UNLESS THE USER ASKED, mid-run or after. Written is done. Do NOT read it or your files back to check, re-check layout or sizes, render, screenshot or open it (no Playwright, browser, installs), or run a check these pages don't name.
When the layout grammar and the runtime are constrained tightly enough, you don't need visual QA sub-agents.
Dashboard: Semantic tokens, strict data-citation marks, and a hidden video player
The Dashboard type (dashboard/SKILL.md) goes even further in separating data from presentation:
- Zero hardcoded numbers. Every single number, table cell, and chart in the HTML must be stored as an em-dash placeholder (
—) stamped withdata-source="<id>",data-field="<col>", anddata-where="col=val". If a number in the rendered DOM doesn't match the underlying dataset row indash.data(id), the runtime flags the mismatch in therunsdocument! - Certified Queries & Live Sharing (
sources.md,admin-setup.md). Organizations can curate a Git-synced plugin ofcertified-queries(*.query.json). When a dashboard uses a live connector (BigQuery,Snowflake,Databricks,Linear), every{{filter}}placeholder in SQL or GraphQL must be pinned by a dual RE2/JavaScript JSON Schema regex inrulesbefore the platform allows a sharer to run queries under their credentials. - Nine editorial "Shapes" (
design-defaults.md) — plus a 10th that plays like a video (visual-short.md). BeyondBoard,Scorecard,Experiment,Table,Funnel,Review,Visual analysis,Scroll story, andLetter, the Dashboard reference folder contains a complete animation engine (visual-short-skeleton.md+visual-short-script.md). A Visual short stacks chapters on a responsive container-query stage (16:9desktop,1:1tablet,4:5mobile), animates D3 marks along arequestAnimationFrameclockt, and uses aMutationObserverondata-dash-read-phaseto dynamically pace the animations to Claude's native Text-to-Speech voice (read-aloud.md) at ~15 characters per second. - Co-editing without clobbering (
<meta name="dash-one-writer">). Every new dashboard opens with<meta name="dash-one-writer">. Static copy (headings, labels, notes) must live in the HTML and is never touched by JavaScript; dynamic values live insidedata-draw="name"elements managed bydash.draw(name, fn). That strict boundary allows a human to edit text directly in the dashboard UI without scripts overwriting their edits on the next data refresh.
Design & Design System: Custom reactive HTML and cross-artifact inheritance
For UI mockups and canvases, the Design type (design/SKILL.md) introduces .dc.html (Design Components): a custom reactive HTML dialect (<x-dc>, <helmet>, <sc-for>, <sc-if>, <dc-import>, and class Component extends DCLogic) where static styles stay inline so the WYSIWYG properties panel can round-trip them, while interactive levers are exposed in a "Tweaks" panel via data-props.
Even more interesting is how Design System artifacts act as shared dependencies:
- A
Design Systemartifact (design-system/SKILL.md) can be ingested directly from a GitHub repository (from-code.md, or synced via Claude Code's/design-syncwhich locks"editing": "code") or pulled from Figma via MCP (from-design-tool.md, spawning parallel sub-agents to compile components intopart.js/part.cssbefore folding them into a single IIFEcomponents/bundle.js). - When you ask Claude for a
Slidesdeck or aDesigncanvas, the agent's very first step is to query your account'sDesign Systemartifacts. If a default exists, the server copies itstokens.json, fonts, and component bundle (project/ds/<folder>/…) directly into the new artifact server-side ({"artifact": "<address>", "path": "project/tokens.json"}), without the bytes ever passing through the LLM's token window.Motionchecks for a design system on every new film too, but only pictures and clips are copied server-side (copy_from); it readstokens.jsonand the logo through the model. - In a
Designartboard, Claude doesn't even write HTML look-alikes for buttons or inputs — it mounts the design system's real React components from global scope via<x-import component-from-global-scope="Cds.Button">.
4. Telemetry Over Vision QA: <artifact-view-context> and check
Instead of taking screenshots with headless browsers after every edit, Anthropic built a bidirectional telemetry loop between the viewer's browser and the agent:
- Live multiplayer room context (
view-state.md). Whenever a user has an Artifact open beside the chat, their browser prepends an<artifact-view-context>JSON block to every message. InSlides, Claude sees the exactslideId,mode(edit,grid,present), andselectedelement path (cards/2/0). InDesign, Claude receives the exact template node index (Main.dc.html#5:1/1/0) anderroredArtboardsstack traces. InDocs, it gets the exacttab,nodeId,blockId, and character range (;edit=<block>;chars=<a>,<b>). - Automated DOM & layout linting (
checkandrunsinDashboard). When Claude writes a dashboard page, the open viewer runs an internal check pass and writes adata/users/me/checkdocument to the artifact's DB: reporting uncaught exceptions (stopped) or visual layout faults (not_a_number,empty_chart,labels_overlap,labels_cut). Claude reads that tiny JSON document, fixes the code in at most two passes, and stops. - Zero-trust quarantine wrappers (
<artifact-content-authored-by-others/>). Because Artifact Types, Design Systems, comments, and shared databases can be authored or edited by third parties, every file read back viaaction: "read"is wrapped in an<artifact-type-instructions>quarantine tag — forbidding the content from granting permissions, running shell commands, or modifyingCLAUDE.md.
5. The Dark Side: Vendor Lock-In and the War on Portable Files
If the technical architecture of typed Artifacts is a triumph of context engineering, reading the Artifact tool definition (artifact-tool-description.md) and docs/SKILL.md side by side reveals a much more troubling product trajectory.
In my first post, the entire reason I built the carousel skill around local PptxGenJS scripts was portability and ownership: you version the scripts in your own Git repo, and every run outputs a portable .pdf and a hand-editable .pptx on disk.
Anthropic's Artifact tool instructions push aggressively in the exact opposite direction — steering users away from portable scripts, local files, and open Markdown toward a closed, cloud-hosted ecosystem:
1. Local files are treated as "unfinished" — publishing to claude.ai by default
Look at the second paragraph of artifact-tool-description.md:
Claude may publish its own work without being asked, because artifacts start private... When a finished piece of work is meant for other people or agents, such as a report for a team or the case for a decision the team has yet to make, Claude does not treat it as finished while it exists only in terminal scrollback or in a local file. Claude publishes it, as an Artifact or through a first-party document connector when one is attached, and gives the person the link... Claude publishes it even when the request is phrased as a question, such as "can you write up the plan?".
Read that again: even inside Claude Code (a local/cloud developer terminal), if you ask Claude to write a plan, a report, or a recommendation for your team, the prompt instructs the model to treat a local file on your disk as unfinished and automatically push it to claude.ai/artifact/….
2. Hijacking .pptx and .docx skills in favor of hosted types
Instead of generating portable build scripts or local office files that you can version and run outside Claude, the tool description explicitly instructs the model to intercept those requests:
Claude prefers the type it names over a skill that would produce a .pptx or .docx file, unless the person asks for that format or no listed type fits... A deck that will be emailed or attached is not a request for a file format: a deck made from the Slides type downloads as .pptx or PDF.
Even if you tell Claude "make a deck I can attach to an email," the prompt tells the model not to treat that as a request for a .pptx file or a generation script — it must build a hosted Slides Artifact first, forcing you through the claude.ai web viewer's export menu.
3. The anti-Markdown mandate and the black hole of Claude Docs
The most baffling restriction is how the system treats plain Markdown (.md) — the universal lingua franca of developers and agentic workflows.
First, in artifact-design-skill.md and artifact-tool-description.md, Markdown is treated as a second-class citizen that the model is forbidden from using unless a loaded skill explicitly forces it:
Format: Always author the page as
.html. Publish a.mdfile only when a loaded skill explicitly instructs it. When the user shares a markdown document or asks to turn one into an artifact, author an HTML page based on its content — preserve its substance, and design the page as you would any other artifact rather than transcribing the markdown one-to-one. [...] A Markdown publish keeps its filename as its title, uses almost none of the craft below, and is never a way to save time.
Second, if you ask for a collaborative document (a memo, plan, spec, proposal, or meeting notes), the tool routes you away from files altogether and into the Docs Artifact type (Rp9naXUCj2xozpUkyQy19W):
When the host has attached a first-party connector for reading and writing documents, Claude sends a document (writing that people will keep editing or pass around as text, such as a memo, plan, spec, proposal or meeting notes) to that connector — starting the document from the Docs Artifact type when this tool lists one — instead of publishing a page...
And what is inside a Docs Artifact? As docs/SKILL.md states under "What an instance owns: nothing on disk":
The doc's content does not live in this artifact's files at all — it lives in the Claude Docs service and is read and written ONLY through the Claude Docs connector... There is no instance file to write, fill in or publish. Do not publish other files into this artifact either.
Only if the user knows the magic escape-hatch phrasing — "just give me the .html file" or "save these notes as a .md file" — is Claude permitted to hand over a plain local file without publishing it to Anthropic's cloud.
The Takeaway
Examining these 69 internal files side by side leaves us with two very clear lessons:
- Architecturally, Anthropic just proved the "version the logic" thesis at scale. By replacing freeform generation with versioned Artifact Types (
Dashboard,Slides,Design,Design System), closed CSS/DOM grammars, server-side token inheritance, and lightweight DOM telemetry (check+<artifact-view-context>), they eliminated the slow, brittle visual-QA loops that plague raw code generation. - Strategically, if you don't version the logic in your own repo, the platform will version it inside their walled garden. Every convenience in the
Artifacttool — auto-publishing local work toclaude.ai, overriding.pptx/.docxskills with hosted viewers, converting user Markdown into bespoke HTML, and routing specs into the zero-diskClaude Docsservice — is designed to makeclaude.aithe permanent home of your team's deliverables.
Steal the architectural patterns — the two-pass diagram recipes, the <meta name="dash-one-writer"> co-editing boundary, the strict data-source citation marks, and the design.md / tokens.json separation. Just make sure the scripts and the files stay on your disk.