The Subframe MCP server exposes tools for the full design surface — pages, components, snippets, design documents, theme — and this skill teaches you when to reach for each one. Each tool returns a URL the user can open to see the result. The heaviest ones (design_page, design_component, edit_component) run as background AI jobs and also return a jobId — pass it to wait_for_jobs if you need to ensure completion.
Treat a Subframe design as an explicit output, not a prerequisite for UI coding. When the user requests a Subframe design artifact or meaningful design exploration, design in Subframe first and then implement with /subframe:develop if requested. For implementation-only requests, edit the code directly.
When to use this skill
Use this skill when the user wants to:
- Create a page or screen in Subframe, materially redesign one, or explore visual directions
- Add or edit a reusable Subframe component or snippet
- Capture design intent or component usage guidance as a Subframe design document
- Update the Subframe project's visual theme (colors, fonts, corners, shadows, typography)
- Remove Subframe pages, components, snippets, or canvases that are no longer needed
The key value: /subframe:design and /subframe:develop bridge coding and design. They work in both directions — create designs while coding and then ensure your code exactly reflects your design.
Picking the right tool
| Intent | Tool |
|---|---|
| Find out what already exists in the project | list_components, list_pages, list_snippets, list_canvases, get_project_info |
| Build a screen the user navigates to | design_page (new) / edit_page (targeted change) |
| Change only the styles of existing page or snippet elements | update_node_styles |
| Build a reusable building block (Button, Card, ListItem) used inside pages | design_component (new) / edit_component (targeted change) |
| Build a small example used inside a design document (e.g. a Button-variants demo) | design_snippet (new) / edit_snippet (targeted change) |
| Write or update written design / usage documentation | write_design_document |
| Change project-wide colors, fonts, corners, shadows, typography | edit_theme |
| Remove a page, canvas, component, or snippet | delete_page / delete_canvas / delete_component / delete_snippet |
MCP access
Discover the Subframe MCP tools first if your client loads them on demand. If no Subframe MCP tools are available, help the user install, enable, or authenticate the server using the MCP setup guide. In Claude Code or Codex, /mcp shows the server's connection and authentication status.
If read tools work but design or editing tools are unavailable, check the user's project permissions. Viewers have read-only MCP access, including screenshots and search; designing or editing requires an editor seat. Explain that permission requirement when it is the cause instead of asking the user to authenticate again.
Find the projectId
Every design tool takes a projectId. Resolve it like this:
- Check
.subframe/sync.jsonif it exists locally. - If no projectId is found, call
list_projects. Each project includes aprojectId,name,teamId, andteamName.- One project: Use it automatically.
- Multiple projects: Always ask the user which project to use. Present each project with its
teamNameto disambiguate. If the user already mentioned a specific team or project name, match it against theteamNameandnamefields — but still confirm before proceeding. Never silently pick a project when multiple exist.
Audit what already exists
Before working on any design, get a picture of the project's current state. On any project where you don't already have explicit knowledge of what's been built, call:
list_components— see which components already exist. Some projects may have pre-existing components, some may not have any components yet.get_theme— see the project's theme tokens (colors, fonts, corners, shadows, typography).get_project_info— see project-level design documents.
This audit is cheap and critical to proper project management.
Verify theme alignment before designing
The first time you're about to call design_page / design_component / edit_page / edit_component / update_node_styles against a given project in this conversation, follow this process:
-
If the project has codebase context (working in a repo, recreating a page, importing a design), locate and read the codebase's theme source —
tailwind.config.*, theme CSS variables, a tokens module. Also read the codebase's most canonical filled component (typicallyButton) to see which token is used where — names and values can match while roles diverge. -
Write the comparison — a brief alignment summary covering colors, fonts, corners, shadows, typography, and role (which token is used for what, on both sides). Flag mismatches even when names/values look the same. Watch specifically for:
- Role divergence — e.g., the codebase's
brandtoken paints focus rings only while a separateprimarytoken paints filled-button backgrounds; the Subframe project's convention may havebrand-primarypainting the filled-button background instead. If you need to verify Subframe-side roles, callget_component_infoon the relevant component to see which tokens it actually references. - Dual-token systems — shadcn-style
primary/primary-foreground,destructive/destructive-foreground, etc., where each role pairs a surface token with a content token. The Subframe theme may not have direct equivalents.
- Role divergence — e.g., the codebase's
-
If anything mismatches, or
get_themeis empty, stop and ask the user if they would like to set up the theme viaedit_themefirst since any design call is at risk of using hardcoded values and breaking the design system source-of-truth promise. Treat this like destructive-deletion confirmation: required even under autonomy / no-clarifying-questions directives. -
If the codebase uses hardcoded values instead of token, the user may be hoping to start using tokens with Subframe. Ask the user if they want to create a theme in Subframe from their hardcoded values or if they would prefer to stick with hardcoded values.
If the project has no codebase context, only the empty-theme check applies — skip the comparison.
When roles diverge, ask the user how they would like to proceed as far as keeping the Subframe roles versus matching their codebase ones. Based on their answer, follow the process outlined in Risk-classify before calling and, if necessary, safe consolidation via alias bridging.
Grounding design calls in real code
Subframe's agent is far more accurate when the call carries raw code than when it carries paraphrase. "Make the primary darker" leaves a guess; pasting --color-primary: oklch(0.55 0.18 250) doesn't. Wherever the codebase already has the source — a component implementation, theme tokens, a similar page — pass it into the call instead of describing it.
design_page, design_component, and edit_component take grounding through the references parameter — a list of { source, usage } entries where source is one of:
{ kind: "subframe", id }— an existing Subframe page, component, or snippet (by ID or name; resolved server-side, no need to inline its code){ kind: "code", content }— raw code from the codebase (the surface to recreate, data types, usage patterns){ kind: "image", url }— a Subframe-hosted upload URL (e.g. an image the user pasted from Subframe). Uploads only happen inside the Subframe app — there is no MCP or CLI upload, so skip this kind unless you were given an upload URL. Local files or arbitrary URLs are rejected; up to 5 images are used per call, each applied per its ownusagenote.
usage says how the generator should use THAT reference — e.g. "match this page's layout and header" or "recreate this mockup exactly". Always write it: the generator sees only the description plus the references, not your reasoning. (edit_page / edit_snippet are node-targeted and take no grounding.)
Default to pasting full files when they exist. Under-including is generally worse than over-including. Head each content with its path:
// src/components/Button.tsx
<full file content>
Group related files as separate code references (component + stories + CSS module), each with its own usage note.
Don't paste what the agent already has. For edit_component, the current Subframe code is already on the server — don't echo it back. Read it with get_component_info so your description can target exactly what differs. Prefer the most efficient form: plain language when the change doesn't depend on any code the agent hasn't seen ("change padding from 4 to 6, add a hover state"); otherwise pass reference code scaled to what's needed — a targeted diff or code snippet when the Subframe code already resembles the target, a full file when handing over a wholesale target, a sibling pattern, or related types — as code references with usage notes. (edit_page and edit_snippet use a different, node-targeted model — see their own sections.)
Soft cap on very large files (~500 LOC combined). When trimming, keep verbatim:
- Prop / type / interface definitions
- JSX structure with
classNamestrings (and anycva/ variant maps) intact - All styling rules — never paraphrase styles ("subtle shadow with rounded corners" → paste
className="rounded-md shadow-sm")
Safe to trim:
- Business handlers, event wiring, data fetching, side effects
- Imports unrelated to structure/styling
- Test- or dev-only blocks
- Comments that aren't load-bearing
If the codebase has no source for what you're designing, describe the design from scratch rather than fabricating a reference.
For component design (design_component, edit_component)
Include in the description:
- The canonical component source file if it exists in the codebase (e.g.
src/components/Button.tsx). - The stories file (e.g.
Button.stories.tsx/.mdx) — exact variants, sizes, states, and the props that produce each. - CSS modules or scoped CSS (e.g.
Button.module.css) if styling lives outside Tailwind classes. - Prop-type files if types are defined separately (e.g. a
types.ts). - Theme tokens the component references — pull the relevant rows from the codebase theme (
tailwind.config.*, CSS variables) and fromget_themeso the agent keeps roles aligned.
For snippet design (design_snippet)
design_snippet takes the same references input as the other design tools — ground it the same way (the codebase implementation the snippet should mirror as code, existing Subframe entities as subframe). edit_snippet takes no grounding context — it's a node-targeted edit (see its section below).
For page design (design_page)
See Preparing references below for the page-specific rule about leaving Subframe component references as-is and inlining everything else. The general grounding rules above (default to full files, paste styles verbatim, soft cap with trimming) layer on top. (edit_page is a node-targeted edit, not a reference-grounded design call — see its section below.)
For theme edits (edit_theme)
See Preparing the edit_theme description under the Theme section. The codebase theme source files belong in the description verbatim — don't paraphrase token values.
Background jobs and wait_for_jobs
design_page, design_component, and edit_component return a jobId alongside their URL. The job runs in the background — the URL is live immediately and allows the user to watch the design populate.
Surface job status to the user. When you kick off a design, tell them you've started ("Designing your settings page in Subframe…") and present the URL. When the job finishes, tell them a relevant message like "✓ Variations are ready to review.". The user already sees live progress in the editor, but they should not have to go to the editor to know when the design is done.
Present the URL verbatim as a clickable link. Opening the URL returned by design_page, design_component, or edit_component shows the design in Subframe, where the user can watch it generate and keep iterating with the agent in agent chat.
When to call wait_for_jobs:
- Before reading back the generated content with
get_page_info,get_component_info,get_snippet_info, orget_canvas_info. The read returns empty/stale state until the job finishes. - Before a downstream design call that needs the new resource as context (e.g., designing a page that should reference a component you just created).
- Before handing off to
/subframe:developif the user immediately asks to implement.
You don't need wait_for_jobs when you're only presenting the URL to the user and stopping there.
wait_for_jobs accepts multiple jobIds at once — batch them when you've kicked off multiple designs. Each result is running, done, error, or not_found. Call in a loop until every job reads done or error. For design_page, the summary reports how many requested pages were actually applied: partial success is done with the count, while zero applied pages is error. Inspect the canvas and decide whether retrying missing variations is useful. A job that stops reporting eventually becomes error; never treat an unverifiable job as completed work.
Pages
Before designing a page
When the user asks you to design, recreate, or redesign a page that uses non-trivial UI components (see What belongs as a Subframe component for what counts):
-
Run the project audit —
list_components(andget_project_infoif you haven't yet). -
Inventory the components the page renders with their status and decision. Output the list verbatim, even when the conclusion seems obvious
- Missing entirely →
design_component - Exists but visually doesn't match the source/spec →
edit_component(existing components keep their identity; existing usages are updated) - Exists and matches → reference directly in the page design
Example:
Button: missing → design_component Alert: missing → design_component SettingsCard: missing → design_component ProfileMenu: exists, matches → reference - Missing entirely →
-
Write the dependency list before any
design_component/edit_componentcalls. For each new or edited component in the batch, list the other components in the batch that it visually embeds. Output it verbatim, even when the list is short. For example:Button: deps=[] Alert: deps=[] SettingsCard: deps=[Button] // footer holds a Save buttonMany components embed other components — a Card with an action footer holds a Button, a Form holds Text Fields, a ListItem holds an Avatar. If you skip writing the list, you will miss these.
-
Group into waves from the dependency list, then run waves sequentially. A component goes in Wave N if all its deps are in waves < N or already ready as-is. Kick off everything in a wave in parallel,
wait_for_jobson the whole wave, then start the next wave.wait_for_jobson the final wave before kicking off the page, so the page design sees up-to-date components.Example: page needs a new
Button, a newAlert, and a newSettingsCard(whose footer renders a Save Button).- Wave 1:
Button+Alertin parallel →wait_for_jobsboth. - Wave 2:
SettingsCard→wait_for_jobs. - Then
design_page.
- Wave 1:
Default to handling all the components the page renders — not just the domain-specific ones. This includes standard components like Button, Input, Alert, Card, Badge, Tabs, Toggle, etc. design_page does NOT have a default component library to fall back on. list_components is the complete list available in the project. If design_page needs a component that doesn't exist, it falls back to inline markup, which doesn't create a reusable component.
If you would need to create/edit more than 3 components in Subframe to design the page, ask the user if they would prefer you to handle all the components, design the page without them, or somewhere in between. When designing any number of components, always run the dependency listing in step 3.
design_page — new pages and redesigns
Use design_page when:
- Creating a new page from scratch
- Redesigning or rethinking existing UI — if there's an existing implementation in code, use
design_pagewhen the user wants to explore new design directions or add new features - Recreating an existing UI from code exactly as a starting point to design in Subframe
- The user wants options to choose from (multiple variations)
Always pass a canvasName (required); variations land as pages on that canvas. Returns canvasId, canvasUrl, and jobId.
Context and variations
How much context to gather and how many variations to generate depends on the task:
| Task | Context | Variations |
|---|---|---|
| New page (open-ended) | Data types (code reference) | 4 — explore the design space |
| New page (with reference pages) | Reference pages (subframe reference if in Subframe, code if not), data types (code) | 1-2 — stay close to the reference pages |
| Redesigning existing UI | The current page (subframe reference if in Subframe, code if not; the usage note says what to keep vs change) | 2-4 — depending on how open-ended |
| Recreating an existing UI | The current page's exact markup and styles (code reference, usage "recreate this exactly") | 1 — recreate the UI from code exactly |
Always include when available:
- The existing page being discussed and similar existing pages (the single most valuable context) — pass them as
subframereferences by ID or name (e.g. a page ID from a pasted MCP link, or a variation page ID the user referenced). Asubframereference resolves any Subframe page, snippet, or component; use acodereference for code that only exists in the codebase. - Components or patterns the user refers to or explicitly mentions —
subframereferences for components already in the Subframe project,codereferences for patterns or code not yet in Subframe - Data types/interfaces for what the page will display (as a
codereference) - A mockup or screenshot the user supplied through Subframe — as an
imagereference (Subframe-hosted upload URLs only)
Preparing references
The general "paste real code, soft cap with verbatim styling" rules in Grounding design calls in real code apply here too. The page-specific addition below is the Subframe-vs-non-Subframe component handling rule.
When including code in a code reference, distinguish between components in the Subframe project vs not (list_components is the source of truth):
- References to components that already exist in the project — leave as-is. Subframe will resolve them from the project.
- References to components that don't exist in the project yet — either design them first with
design_component(see Before designing a page), or inline their rendered JSX + Tailwind classes into the reference content. - References to non-component application code — always inline. See What belongs as a Subframe component for the distinction.
When you inline a component, expand it into its JSX markup (inputs, buttons, layout, Tailwind classes). If that expanded markup has more component references, evaluate those using the same process.
Variations
Each variation is an object with a name (short name) and description (a few sentence prompt describing the design direction).
When you have reference pages (references), use fewer variations (1-2) and keep them grounded in the reference. The variations should refine or extend the existing design, not diverge from it. For example:
{ "name": "Adapted layout", "description": "Follow the same layout as the reference page but adapted for [new content]" }{ "name": "Compact data-dense", "description": "Same structure as the reference but with a more compact, data-dense layout." }
When starting from scratch (no references), use more variations (4) to explore the design space:
{ "name": "Data table", "description": "Compact data table with inline actions and bulk operations." }{ "name": "Card grid", "description": "Card-based layout with visual hierarchy and quick filters." }{ "name": "Minimal single-column", "description": "Minimal single-column design focused on the primary action." }{ "name": "Split panel", "description": "Split-panel layout with sidebar navigation and detail view." }
More variations = more exploration. Fewer = more focused. Default to fewer when strong context exists.
Multi-page requests
When designing multiple related pages (user flows, CRUD, etc.):
- Design the primary page first with more variations to establish the direction.
- After the user has reviewed the variations on the canvas, design each remaining page passing the chosen page's ID as
sourcePageId— the generation starts from that page's EXACT structure and changes only what the description asks, so shared header, nav, and layout carry over by construction. Have the user paste an MCP link to the variation they want, or useget_canvas_infowith thecanvasIdto enumerate the pages on the canvas and ask which to use. Addreferencesfor anything else the new screen should draw on. - Use the same
canvasNameso related pages land on the same canvas. Don't pack multiple screens into variations — variations are independent alternatives of the SAME screen.
update_node_styles — restyle elements on a page or snippet
Prefer update_node_styles when only the Tailwind styles of existing page or snippet elements need to change. It updates classes in place while preserving content, structure, instance props, and node IDs. Untargeted descendants are unaffected. To change a reusable component definition, use edit_component; page layouts must be edited in the Subframe editor.
- Call
get_page_infoorget_snippet_infowithincludeNodeIds: trueto read the current classes and each element'sdata-node-id. - Identify the page or snippet with
id,name, orurl, and pass itsprojectId. - Pass
nodeIds(an array of 1–100 node IDs) and the complete newclassName. Use a one-item array for a single element.
The class list is replaced in full. Include every class that should remain, including sizing, layout, responsive, and state classes. For example, changing the padding of flex w-full flex-col gap-4 p-4 requires flex w-full flex-col gap-4 p-6, not just p-6.
Batch elements from the same page or snippet only when they should share the entire new class list. If their final classes differ, use separate calls even when the requested change is the same.
The edit applies immediately and returns url plus results, one entry per unique target in request order. Check each entry's nodeId, appliedClassName, and optional warnings for dropped or unsupported styling. Canonical classes may be normalized; compare the intended styles, not just the exact string.
edit_page — targeted edits to an existing page
Use edit_page for targeted changes to an existing page element's content, structure, styles, or a combination of these. It takes a structured edit:
- Call
get_page_infowithincludeNodeIds: trueto get the page JSX with adata-node-idon every element. - Pick the
nodeIdyou want to change. - Choose an
operation:replace(swap the node and its subtree),insert-above/insert-below(add yourcodeas a new sibling), ordelete(remove it). - Pass the new or replacement subtree as
code— a JSX fragment with a single root element. Don't includedata-node-idattributes (new nodes are assigned ids automatically); leavecodeempty fordelete.
replace removes the target and its entire subtree. Include every descendant that should remain in the replacement JSX. Never send an empty replacement wrapper just to change styling.
Identify the page with id, name, or url (call list_pages first if you need to find it). The edit applies immediately and returns pageUrl, appliedCode (the canonical code after parsing — compare it to what you sent to confirm nothing was dropped or normalized), and any parser warnings. appliedCode carries each element's data-node-id, so target those nodes in follow-up edits directly instead of re-reading the page. The user can undo via page version history in the Subframe editor.
code must be valid Subframe JSX: a single root element and static markup only — no .map(), hooks, state, or conditional rendering (these are rejected, not silently dropped), and no <html>/<body> wrappers. Style with Tailwind classNames; inline style={{…}}, event handlers, and most data-* attributes are dropped. The includeNodeIds: true output is already in this shape — use it as your template.
When to use edit_page vs design_page
edit_page: Targeted changes to an existing Subframe page.design_page: New pages, redesigns, or exploring multiple design directions.
When NOT to use edit_page: If the user has existing UI in their codebase but no corresponding Subframe page, or if they want to explore multiple design options, use design_page instead.
After a page design
For design_page, present the returned canvasUrl as a clickable markdown link. The canvas opens immediately; each variation appears as a new page on the canvas as it finishes generating. The user reviews them side-by-side on the canvas and may keep multiple, edit them, delete some, or just leave them all there — there's no formal "pick one" step.
From there, the user may continue refining in Subframe or return here and ask you to implement the design in code. Do NOT ask the user which variation they prefer or present variation options as a multiple choice in chat. Simply present the canvas URL and let them know they can ask you to implement once they're ready.
If you need to enumerate the variation pages programmatically (e.g., to reference one in references or sourcePageId, or to read its current code with get_page_info), call wait_for_jobs with the jobId first, check its applied-page count, then call get_canvas_info with the canvasId. Reading too early may return only the variations that have finished by that moment.
Internally track the canvasId returned by design_page. Don't surface it to the user. Use it with get_canvas_info for follow-up canvas-level operations, or pass the same canvasName on subsequent design_page calls so new variations land on the same canvas.
When referencing, implementing, or editing a page, use specific page IDs the user has referenced (via pasted MCP link or while iterating in the editor), or call get_canvas_info to look them up by name — design_page itself doesn't return individual page IDs since successful variations land as separate pages on the canvas.
Components
Components are reusable UI building blocks (Button, Card, ListItem, Toggle, etc.) that get used inside pages. They live at the project level and sync into the codebase via npx @subframe/cli sync. Designing a component creates a new entry in the project's component library. Editing one updates every page that uses it.
What belongs as a Subframe component
Subframe components are visual/presentational primitives — the reusable UI building blocks that get composed into pages. Be deliberate about what gets promoted to a component vs. what stays inline in a page.
Make it a component if it:
- Renders pure UI: buttons, inputs, cards, modals, badges, alerts, list items, layout primitives (containers, stacks, grids), etc.
- Will be reused across multiple pages, or has variants worth defining once
Keep it inline (in the page, not a component) if it:
- Fetches data, calls APIs, or manages application state
- Wires together business logic (form submission handlers, validation flows, page-level orchestration)
- Is a one-off composition specific to a single page
- Is utility code (hooks, helpers, non-visual modules) — those don't belong in Subframe at all
When unsure, quickly read the source. If it imports data-fetching libraries, stores, or API clients, it's application code — keep it in the page.
design_component — add a new component
Use design_component to create something that should be a Subframe component (see What belongs as a Subframe component). Pass:
description— what the component is and how it should look/behave.references(optional) — the grounding material with per-reference usage notes. Pass real code, don't paraphrase — see Grounding design calls in real code for what to include (canonical source, stories, CSS modules, prop types, relevant theme tokens) and verbosity rules, plus a mockup/screenshot as animagereference when the user supplied one through Subframe. Apply the Subframe-vs-application rule from Preparing references: pass components that already exist in this Subframe project assubframereferences, inline anything else ascode.name— the component name (PascalCase, e.g., "PrivacyToggle")projectId— usually inferred from.subframe/sync.json
Returns componentId (immediately referenceable in other tools), componentUrl (open this in the editor to watch the design happen), and jobId (pass to wait_for_jobs before reading back via get_component_info or referencing in another design call).
edit_component — change an existing component
Use edit_component for targeted changes to a component already in the project. Call get_component_info first so your description can target exactly what differs. The agent already has the current Subframe code — only paste outside reference code when the change depends on something the agent can't see (a codebase implementation to match, a sibling component, a design spec). See Grounding design calls in real code for what to include and how to trim.
Pass one of id, name, or url plus a description; optionally references to point the agent at related Subframe pages/snippets/components, outside code, or an image to match. Returns componentUrl and jobId. Edits propagate to every page using the component, so confirm with the user before making structural changes.
The same component cannot be edited by two agents simultaneously — if another conversation is already working on it, the tool returns the in-progress URL and you should wait or ask the user.
Note: Agent editing is not supported for page layouts. To modify a layout, the user must open it in the Subframe editor directly.
Use edit_component to align existing components with a codebase or spec. If a project component doesn't match the user's source code or design references, that's an edit_component job — don't design a parallel one. If it's unclear whether the edits apply cleanly to existing usages, confirm with the user before editing.
Snippets
Snippets are small, standalone bits of UI typically embedded inside design documents as live examples — for instance, a "Button variants" snippet showing every Button state side-by-side, embedded in the Button's usage doc. They can be inserted into any design but are detached whenever inserted, so changes to a snippet do not propagate to other designs. They're not synced into code as components.
design_snippet — create a new snippet
Use design_snippet when the user wants to illustrate something in a design document, or wants a small standalone composition that doesn't need to be represented as a component. Pass:
description— what to showname— optional; defaults to "AI Generated Snippet"references(optional) — the same grounding input as the other design tools: outside code ascodeentries (the codebase implementation the snippet should mirror, related types, the usage example it illustrates), existing Subframe pages/snippets/components assubframeentries (resolved server-side, no need to inline their code), plus animagewhen the user supplied one through Subframe — each with ausagenote.
Returns snippetId and snippetUrl. Embed the snippet in a design document with <div data-type="component-example" data-component-id="<snippetId>"></div> (see the design documents section).
edit_snippet — change an existing snippet
Same node-targeted model, JSX constraints, and replacement rules as edit_page, but for snippets. Call get_snippet_info with includeNodeIds: true to see each element's data-node-id, then pass nodeId, an operation (replace / insert-above / insert-below / delete), and a single-root code fragment. The edit applies immediately and returns snippetUrl, appliedCode (with each element's data-node-id for follow-up edits), and any warnings. Use when the embedded example needs to evolve alongside the component it documents.
Design documents
Design documents are markdown files that convey how to work within your design system — brand voice, design principles, component usage rules, accessibility requirements, do/don't examples. They're read by you (and other coding agents) when designing or implementing. There are two kinds:
- Project-scoped docs — cover broad guidance like design principles, project-wide conventions, onboarding notes. A project can have many.
- Component-scoped docs — attached directly to a specific component; cover specifics for that component like "when to use this" and do/don't examples. A component can have at most one design document.
Use design documents when:
- The user explicitly asks for one ("write a design doc for the Toggle component")
- There is complexity in using the design system that needs to be documented for future designs
- The user's repo has existing design guidelines that should be migrated to Subframe
- A component needs usage guidance, accessibility notes, or do/don't examples
Read existing docs first via get_project_info (returns project-level docs) or get_component_info (returns the component's designDocuments). If a component already has a doc, you must update it (don't try to create a second one); pass the existing id to write_design_document.
What belongs in a design document
Design documents are for design judgment that Subframe's structured data can't carry. They should be concise and contain information that is unobvious to a consumer of the design system. Subframe's agent already has access to:
- Component code (props, JSX, styles)
- The theme (colors, fonts, corners, shadows, typography tokens)
Restating any of that in a doc is wasted space. Reach instead for the layer above: when to use what, how to compose, what it should say, what to avoid.
Belongs in a design doc:
- Usage hierarchy and taxonomy
- Variant meaning — what each variant communicates to the user
- Sizing rules
- Labeling rules
- Composition patterns and accessibility notes
- Theme conventions — when to use the existing theme tokens, e.g. use brand color here but neutral there
- Do's and don'ts framed as design judgment
Does NOT belong in a design doc:
- Prop tables or API documentation — the agent reads the component code already
- Theme token values (hex codes, pixel spacing, shadow definitions, radius values) — the theme already holds these. If the project's theme is wrong or empty, fix the theme via
edit_theme; don't paper over it in a doc. - Inline JSX or Tailwind class examples — these should be embedded snippets instead
- Common design standards — if any designer would follow a pattern by default, don't restate it. Only document practices unique to this design system. When a common standard does hold particular importance, write the project-specific angle ("Confirm before destructive actions — restate the noun in the dialog, e.g. 'Delete survey' not 'Are you sure?'"), not the standard itself.
- Anything trivially derivable from the component source or the theme
write_design_document
Inputs:
content— markdownid(optional) — if editing an existing doc; omit to create a new onecomponentId(optional) — if creating a new component-scoped doctitle(optional) — for project-scoped docs only; ignored for component-scopedmode(optional) —replace(default) overwrites,appendadds the new content after existing
Embed snippet examples with HTML:
<div data-type="component-example" data-component-id="<snippetId>"></div>
Preserve these tags verbatim when round-tripping through mode: replace — losing them removes the embed.
Theme
Use edit_theme to update the project's visual theme — colors, fonts, corners, shadows, typography tokens. Applies immediately to the whole project; there's no preview step. Always use get_theme first to see the current state before formulating changes.
What edit_theme can do
- Add new tokens
- Tweak values on existing tokens ("make the primary darker," "increase border radius")
- Rename tokens — safe. Components and pages reference tokens by id, so the alias survives the rename.
- Delete tokens — destructive. Every page/component that referenced the deleted token gets its alias replaced with the token's concrete value at deletion time. Those usages are now detached (hardcoded), not tracking any token. Theme version history restores the token, but the detached references stay detached — full recovery requires project version history.
Preparing the edit_theme description
When the project has codebase context, the description should carry the actual theme source files verbatim, not paraphrased token values. Include every token — do not drop tokens that seem unrelated to the immediate task. The theme is project-wide; what you are about to design isn't the only consumer. Look for:
tailwind.config.*(oftentailwind.config.tsor.js)- Global CSS files with
@themeblocks (Tailwind v4) or:root { --color-... }declarations —globals.css,app.css,index.css - Token modules (
tokens.ts,tokens.json,theme.ts, Style Dictionary exports) - Font config (
next/font, CSS@font-face, font import URLs)
Paste each file in a fenced block headed by its path (// tailwind.config.ts). Don't summarize token values — the agent's accuracy on color, spacing, and typography depends on the exact strings.
Only let the agent invent tokens when there is genuinely no codebase theme source. In that case, say so explicitly in the description.
Risk-classify before calling
Read the user's prompt and classify before invoking:
| Risk | Operations | Action |
|---|---|---|
| Low | Adding tokens, tweaking values | Call directly; mention what changed after |
| Medium | Renaming tokens, broad multi-token tweaks | Briefly note the scope (whole project) and call |
| High | Anything that deletes tokens, replaces the palette wholesale, or implies "remove / strip / replace existing X" | Confirm with the user before calling. Spell out what will be deleted and that pages/components using those tokens will be updated to use hardcoded values (which would then require project version history to undo). |
When in doubt about the risk of a prompt, assume High risk and confirm.
Safe consolidation via alias bridging
When the user wants to consolidate tokens (e.g., brand-50 through brand-900 → brand-primary / brand-secondary / brand-tertiary), use alias bridging to avoid the destructive-delete cascade for tokens that map cleanly to the new structure. The process takes two sequential edit_theme calls (can't be combined into one):
- First call — create the new tokens AND re-point each mappable old token as an alias of its matching new token. Example: create
brand-primarywith the desired color, and updatebrand-200to aliasbrand-primaryin the same call. - Second call — delete the old tokens. Because references in pages/components resolve through the alias to the new tokens, they stay attached — no hardcoded fallback.
When alias bridging doesn't apply: some old tokens won't map cleanly to any new one (e.g., brand-50 is too pale to belong to any of primary/secondary/tertiary). For those, the standard destructive-delete cascade applies — references become hardcoded values. Make a per-token call: bridge what you can, accept detachment (and confirm with the user) for what you can't.
When NOT to use edit_theme
- Styling individual pages or snippets — edit those resources directly; theme token changes apply project-wide.
Deletion
Four tools, one per resource type. Always confirm with the user before calling any delete tool — these are irreversible from MCP (the Subframe editor retains version history for restore, but recovery is manual and may require reverting changes that occurred after).
delete_page({ id|name|url, projectId, force? })— deletes a page, removing it from its canvas and stripping prototype actions referencing it. Refuses by default if referenced in other pages. Useforce: trueto delete anyway. Page layouts can't be deleted with this tool — usedelete_component(it cascades to clearpageOptions.layouton every page using the layout).delete_component({ id|name|url, projectId, force? })— deletes a component or page layout. Detaches instances or clears layouts. Refuses by default if in use. Useforce: trueto delete anyway.delete_snippet({ id|name|url, projectId })— deletes a snippet. Any design document embeds are removed automatically.delete_canvas({ id|name|url, projectId, deleteChildPages? })— deletes a canvas. Refuses if it has pages on it. UsedeleteChildPages: trueto delete the canvas plus every page on it.
When a delete tool refuses because of references, surface what it would affect to the user before retrying with force: true / deleteChildPages: true. Don't auto-escalate to force-mode without confirmation.
Iterating
The user reviews and refines designs in the Subframe editor, not in code. When they come back asking to combine ideas, refine a specific direction, or iterate further:
- They reference a specific variation (by pasted MCP link, by name, or by describing it). If you need to find the variation's
pageId, callget_canvas_infowith thecanvasIdfrom the originaldesign_pageresponse — it returns the pages on the canvas with names and IDs. Make targeted edits to that page, or calldesign_pagewith the page assourcePageId(to evolve its exact structure) or as asubframereference (for a fresh set of options grounded in that direction). - They want to mix variations ("I like the layout from variation 1 but the colors from variation 3"). Ask them to paste the MCP links of the variations they want to combine (or use
get_canvas_infoto look up page IDs by name), then calldesign_pagewith those pages assubframereferences whose usage notes say what to take from each, and a description of the combination. - They want to start over ("none of these are right"). Call
design_pageagain with a refined description and any reference pages assubframereferences. Use the samecanvasNameto keep related work on the same canvas. - They want to iterate on a component or snippet. Use
edit_component/edit_snippetfor targeted changes; the resource keeps its identity and existing usages stay wired up.
You don't have to read the generated code by default — Subframe renders the designs and the user reviews them visually in the editor, so summarizing them in chat usually isn't useful. When reading the code would genuinely help (the user asks what was generated, you're picking which design to extend, etc.), call wait_for_jobs if necessary and then the required get_*_info calls.