Quick Start
Add Bezel to an app you already have. By the end, your tokens come from your Bezel project, compile to CSS variables on every build, and, if you want it, update live in the preview panel while you edit.
You'll need Node 22+, a Bezel project with some tokens in it, and a Pro or Enterprise plan for MCP access.
With the MCP server installed, ask your agent to do the setup for you:
Set up Bezel in this project.
Set up this project to use my Bezel project named Acme Design System.
Configure Bezel Kit in this repo and generate my variables.css.
Use the Bezel MCP to set up this project with my Bezel project <uuid> at version 1.4.0.
Or run /mcp__bezel__setup-project (optionally /mcp__bezel__setup-project "Acme Design System").
The agent lists your Bezel projects, you pick one, and it completes steps 1–4 below, skipping
anything the repo already has. It asks before editing package.json or your entry file. The manual
steps below remain the reference for what it does.
1. Install
npm install --save-dev @bezel-labs/bezel-kit
2. Pull your tokens
Bezel reads one file: design-tokens.json at the root of your project. The name and location are
fixed, so put it there.
Using the MCP server
Connect your editor once. In Claude Code:
claude mcp add bezel --transport http --url https://mcp.bezel.new/mcp
Run /mcp and authenticate. Bezel uses OAuth, so there's no API key to copy around.
Then ask for the project by name:
Sync my Bezel tokens from my project named Acme Design System.
That runs the sync-project prompt. Your agent resolves the name with find-project (asking if
more than one matches), confirms the project with you, and writes its projectId and version to
bezel.json. It then fetches the file with get-design-tokens-file and writes design-tokens.json
exactly as the app's Download button would:
{
"$schema": "https://www.designtokens.org/schemas/2025.10/format.json",
"base": { "color": { "…": {} } },
"semantic": { "color": { "…": {} } }
}
Finally it runs bezel build so your generated outputs match.
Later runs don't need the project name. The agent reads projectId from bezel.json and just asks
for the latest tokens. To pin a published release instead of latest, set version in
bezel.json (see the next step) or ask for it: "Update my tokens to version 1.4.0."
If the repo isn't set up yet (no bezel-kit or no bezel.json), sync-project says so and runs
the setup flow from the Fastest path tip at the top of this page first.
Without MCP
Click Download in the app header and save the file to your project root. Same result, done by hand.
3. Create bezel.json
npx bezel init
That writes the config for you, choosing defaults from your project:
{
"projectId": "0b5c3d1e-7f2a-4c9b-8e6d-1a2b3c4d5e6f",
"version": "latest",
"variablesOutput": "src/bezel/variables.css",
"contextsOutput": "src/bezel/contexts.ts",
"fontsOutput": "src/bezel/fonts.ts"
}
The first two fields are for the MCP server; bezel build ignores them. projectId is the Bezel
project this repo tracks, so your agent never has to ask which one you mean. version is which
tokens to pull: latest (the default init writes) means your current working tokens, and a
published semver like 1.4.0 pins that release. Pass them on the command line to have init fill
them in:
npx bezel init --project <uuid> --tokens-version 1.4.0
Both flags also work on an existing bezel.json without --force: each updates only its own key
and leaves the rest alone.
Outputs go to src/bezel/ (or bezel/ if you have no src/). Everything Bezel writes is generated
and gitignored, and bezel build overwrites without asking. Its own directory means it can never
land on a file you wrote.
Only variablesOutput is required. The other two are small TypeScript modules listing your themes
and the web fonts your tokens use, handy for theme switchers and font loading. init skips them if
your project has no tsconfig.json.
Override anything inline, or just edit the file afterwards:
npx bezel init --dir packages/ui/src/bezel --no-fonts
Apart from --project and --tokens-version, init won't touch an existing bezel.json unless
you pass --force.
If any token in the file has an export
name, Bezel switches to export-name mode: a token with exportName: ["primary"] becomes
--primary, and any token without one emits nothing at all.
If no token has one, names come from the token path with base/semantic and default trimmed
off, so semantic.color.primary.default becomes --color-primary.
If your generated CSS comes out emptier than expected, this is why. Set export names in the token editor first.
4. Wire it into your scripts
{
"scripts": {
"tokens": "bezel build",
"dev": "npm run tokens && vite",
"build": "npm run tokens && vite build"
}
}
bezel build reads design-tokens.json, picks up bezel.json automatically, writes your outputs,
and adds them to .gitignore in a managed block. Generated files are build output, so don't edit them
and don't commit them.
Import the CSS once in your entry file:
import './bezel/variables.css';
Run npm run dev and use the variables:
.button {
background: var(--primary);
color: var(--primary-foreground);
}
Each context becomes its own scope: the base context is :root, and others are classes (.dark,
.light). Switch themes by toggling that class on <html>.
5. Live preview (optional)
This renders your app inside Bezel's preview panel, with token edits applied instantly. It's a development tool, so install it as a dev dependency and keep it out of production builds.
npm install --save-dev @bezel-labs/crystal
In your entry file, before you render:
/**
* Live preview (dev environment only example)
*
* Lets the Bezel preview panel push token edits into this app while you work.
* It plays no part in how the app is built or styled: your real styles come
* from the generated variables.css, so removing this block changes nothing
* except that the panel stops updating live.
*
* The dynamic import sits inside the guard on purpose. That's what keeps
* both the listener and the @bezel-labs/crystal code out of your production
* bundle, since the bundler drops the whole unreachable branch.
*/
if (import.meta.env.DEV) {
import('@bezel-labs/crystal').then(({ startLivePreviewReceiver }) => {
startLivePreviewReceiver({
allowedOrigins: ['https://app.bezel.new']
});
});
}
On Next.js or anything webpack-based, use process.env.NODE_ENV !== 'production' in place of
import.meta.env.DEV.
Keep allowedOrigins explicit. The receiver validates every incoming message against it, then
injects the CSS into a single <style> tag so it cascades over your generated variables.css.
Now point the panel at your app: in the preview panel, open Preview URL, enter your dev server
URL, and save. http://localhost:5173 works fine.
Edit a token in Bezel and your app restyles without reloading.
The guard above means a production build won't respond to the
panel. If you want to preview a deployed staging site, run the receiver there too, and list only
the origins you trust, since it accepts CSS from anything in allowedOrigins.
If your app renders the real content inside a second
iframe, the outer frame needs createLivePreviewRelay to forward messages one hop down. See
Live Preview.
Command reference
| Command | What it does |
|---|---|
bezel init | Create bezel.json with defaults inferred from your project |
bezel init --dir <path> | Put the generated outputs somewhere else |
bezel init --force | Overwrite an existing bezel.json |
bezel init --project <uuid> | Set projectId, the Bezel project the MCP syncs from. Updates an existing file in place |
bezel init --tokens-version <v> | Set version to latest or a published semver. Updates an existing file in place |
bezel build | Generate outputs from design-tokens.json + bezel.json |
bezel build --stdout | Print the CSS instead of writing files, good for a quick check |
bezel build --color hex | Emit hex colors instead of the default oklch |
bezel build --unit rem | Convert px dimensions to rem |
bezel build --no-gitignore | Leave .gitignore alone |
bezel --help | Everything else |
Don't set nameExtension in bezel.json if you're using live
preview. The panel pushes CSS built with the default, so a custom value produces different variable
names and live edits will quietly stop matching your build output.
Keeping tokens up to date
Once the repo is set up, refreshing design-tokens.json is one prompt. Any of these runs
sync-project:
Update my design tokens.
Sync my Bezel tokens.
Pull the latest tokens from Bezel.
Or /mcp__bezel__sync-project. The agent reads projectId and version from bezel.json,
fetches the file, writes it, and runs bezel build. No project name needed.
To point the repo at a different project, say so and it updates bezel.json after confirming:
Update my <app name> project to use my Bezel project named Acme Design System.
Switch this repo to Bezel project <name or uuid>.
To move to a published release:
Update my tokens to version 1.4.0.
Or /mcp__bezel__sync-project "Acme Design System" 1.4.0. More in
MCP Workflows.
Next steps
- Core Concepts — tokens, groups, and contexts
- MCP Server — the rest of the tools your agent can call
- Live Preview — templates, themes, and responsive views
- Publishing — versioned releases your team can pin to