Design systems
A design system tells Statecraft where your components come from and how to render them. The import agent writes most of this for you, so the job that actually lands on your desk is reviewing what it proposed and fixing it when it's wrong. This page is about that job. The field-by-field schema is in statecraft.yaml.
Five ways a library can load
Every design system has a source.kind. The import agent picks one for you; it helps to know which you ended up with, because it determines what can go wrong.
| Kind | What it is |
|---|---|
live | Your repo, compiled to a bundle by Statecraft and re-uploaded whenever it changes. This is what Import produces and what almost every real team wants — it's the only kind that stays in sync with the code your engineers are writing. |
npm | A published package, fetched from a CDN at render time. Good for off-the-shelf libraries (MUI, Ant, Radix) that you consume unmodified. |
css | A stylesheet, with thin wrapper components synthesised around the class names. For CSS frameworks — Bootstrap, Bulma, your own BEM kit — where there are no components to import. |
none | Plain HTML elements. What a blank scratch project starts on. |
host | A pre-built ESM bundle you host yourself. The escape hatch when you want to own the build. |
Correcting the import agent
The agent reads your repo, proposes a manifest, builds the bundle, and headless-mounts every component to check it renders. Then it hands you the result. Correct it in plain English and it rebuilds:
the package lives in packages/ui, not the repo root drop Skeleton and the cva() helpers from the component list components need the ThemeProvider from src/theme.tsx wrapped around them tailwind config is at config/tailwind.config.js
Because every correction triggers a real build, a proposal that reaches Publish has already compiled and rendered. What it can't know is whether the component list is the one you want — that part is a judgment call, and it's the part worth your attention.
Components render, but unstyled
The most common failure, and rarely a Statecraft bug. Library components seldom carry their own styling context — it lives in the app shell: a global stylesheet the entry point never imports, a ThemeProvider, a CSS reset.
Reproduce that shell with a frame. Point build.frame at an existing wrapper module in your repo, or inline one with build.frameSource when no such file exists. Statecraft wraps every rendered state in it. (Tailwind globals are different — those go through the tailwind.build block.)
The frame belongs in your manifest, not in your repo's source tree. Statecraft never asks you to add files to your codebase for its own benefit — frameSource exists precisely so it doesn't have to.
The palette is empty
Two fields control what you can place, and they do different jobs:
scope:— which exports are legal as JSX tags. Without an entry here, a component doesn't exist as far as a state's source is concerned.components:— which components appear in the palette, with the snippet inserted when you drag one out.
A scope: with no matching components: entry gives you a design system that renders perfectly and has an empty palette — nothing to drag. The Components panel warns you about this, and it's easy to skim past.

Statecraft never writes scope: for you — guessing wrong would silently expose hooks and cva() helpers as broken pseudo-components. Instead it writes the bundle's real exports to _design-systems/<slug>.exports.txt in your sync folder as a list to copy from.
Where the file lives, and who owns it
For a kind: live design system, statecraft.yaml in your repo is the single source of truth. Both the desktop daemon and statecraft publish read the same file, so there is no second config to keep in sync — and no live update command. You edit the file and save.
Once a row has been published from CI or mirrored by the desktop app, the in-app YAML editor goes read-only and shows the manifest verbatim. That's deliberate: the file wins, and letting you type into the editor would only mean your next publish silently overwrote it.
Read-only applies to the YAML, not to the design system. The YAML says which components are in scope and what their tokens are; it was never where a component's appearance lived. To change that, open the component itself: each row in the Components list has an Edit button, which turns the component into a project whose frames are its real variants. Your edits stay pending against the design system until you hand them off as a pull request — so the repo remains the source of truth, and nothing changes for your teammates until someone reviews it.
If you only want to experiment without touching the canonical library, the read-only banner still offers Duplicate — the fork lands as an ordinary editable row.

Did it actually work?
Don't trust a green spinner — read _design-systems/_status.md in the sync folder. It's the canonical answer to "did my import/publish succeed", written after every build, and it names the failure when there was one. The CLI's --wait flag and non-zero exit codes carry the same signal for scripts; see CLI & desktop app.
When you're ready to have engineers' changes flow into Statecraft automatically, wire up publishing from CI.