Doop
Doop: 오픈소스 멀티플레이어 디자인 캔버스 및 AI 에이전트용 MCP
Design token editor with live CSS variable editing. Svelte 5 + Vite.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A design system for styling and building Svelte + Vite microsites. Edit tokens and components in a dev-only editor and watch the running site repaint on every input. Save the result as a theme file and carry it between projects.
The editor is dev-only. Production builds get plain CSS variables and the components you used.README 전문 보기 ↗A design system for styling and building Svelte + Vite microsites. Edit tokens and components in a dev-only editor and watch the running site repaint on every input. Save the result as a theme file and carry it between projects.
npm install @motion-proto/live-tokens
The editor is dev-only. Production builds get the components you used, and their CSS keeps a variable only for a token whose value can change while the page runs.
:global(:root) block. Rewire an alias from the component's editor and it updates everywhere that component is used, on your real pages./live-tokens/editor for tokens, /live-tokens/colors for palettes, /live-tokens/components for per-component aliases, /live-tokens/docs for the user guide..svelte file in VS Code./api/live-tokens/{colors-and-type,component-configs,themes,sketch-styles}/* routes the editor reads and writes through. The single namespace keeps these routes clear of anything your app serves under /api.// vite.config.ts
import { defineConfig } from 'vite';
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import { themeFileApi } from '@motion-proto/live-tokens/vite-plugin';
export default defineConfig({
plugins: [
// vitePreprocess compiles the shipped components' `<style lang="scss">`
// blocks. Install `sass` alongside it.
svelte({ preprocess: vitePreprocess() }),
themeFileApi({
tokensCssPath: 'src/system/styles/tokens.css',
}),
],
});
The themeFileApi plugin:
default colors and type from the installed package, so you start on the shipped defaults with no local copy.src/components/*.svelte and src/system/components/*.svelte, and seeds src/live-tokens/data/component-configs/{comp}/default.json from each component's :global(:root) block.src/live-tokens/data/themes/default.json at dev-server start: the Default theme, derived from the shipped colors and type plus those component defaults, and regenerated when either changes. It is protected, so the editor never deletes it and an outside deletion heals on the next start.tokens.generated.css from the production theme at startup, so a fresh checkout builds against the look you shipped./api/live-tokens/* routes the editor saves and loads through.__PROJECT_ROOT__ for the overlay's "Page Source" link and __LIVE_TOKENS_API_BASE__ so the client uses whatever apiBase you configured.A project last opened on 0.47.1 or earlier keeps its colors and type in themes/ and its whole looks in manifests/, the names 0.48 reassigned. The plugin recognises that layout, writes nothing, and says so. npx live-tokens migrate moves themes/ to colors-and-type/ and manifests/ to themes/, records what the retired per-layer pointers resolved to as the production theme, and clears them. Restart the dev server afterwards.
main.ts// main.ts
import '@motion-proto/live-tokens/app/tokens.css';
import './live-tokens/data/tokens.generated.css';
import '@motion-proto/live-tokens/app/fonts.css';
import { bootLiveTokens } from '@motion-proto/live-tokens';
import App from './App.svelte';
bootLiveTokens(App, '#app');
bootLiveTokens runs the editor's idempotent init hooks, fetches the active theme in dev, registers any consumer-authored components, and mounts the app. It side-effect-imports FontAwesome, because the dev overlay always needs icons. The three token-CSS imports stay with you: order matters, and tokens.generated.css is project-local.
<LiveTokensRouter><!-- App.svelte -->
<script lang="ts">
import { LiveTokensRouter } from '@motion-proto/live-tokens';
</script>
<LiveTokensRouter pages={{
'/': { lazy: () => import('./Home.svelte'), label: 'Home', icon: 'fa-home', source: 'src/Home.svelte' },
}} />
<LiveTokensRouter> owns the dev overlay (<LiveEditorOverlay> and <ColumnsOverlay>), the four editor routes, in-app link-click interception, and the nav-rail and page-source plumbing the overlay needs. Each entry in pages is one of your routes; entries with a label appear in the overlay's nav rail. Pass lazy: () => import('./Page.svelte') so each page's stylesheet side-effects evaluate only when that route is visited, or component: PageComponent for an eager import. The editor routes dispatch internally, so you never import the library's editor pages yourself.
For routes you cannot enumerate ahead of time (a /:id, a path prefix, a page shown only under some condition), add a resolve function from the current path to a RouteEntry and return null to fall through. Resolution order is pages[path], then resolve(path), then the pages['/'] fallback, so adding resolve never changes how existing entries match. A resolved entry can carry props, letting one component serve many paths, and its source gives the dynamic route a working "Page Source" button.
Link-click interception follows the same route table. A left-click becomes an in-app navigate() only when the anchor asks for ordinary same-tab navigation (no target, download, rel="external", or modifier key) and pages or resolve claims the path. Anything else keeps the browser's own handling, so a link to a PDF or an image under public/, to a download, or to a path no route declares loads for real. Note that the pages['/'] fallback renders an unmatched path without claiming it: link to a path no route declares and you get a page load, not a client-side swap.
<LiveTokensRouter
pages={{ '/': { lazy: () => import('./Home.svelte'), label: 'Home' } }}
resolve={(path) => {
const m = path.match(/^\/module\/(.+)$/);
if (!m) return null;
return { lazy: () => import('./ModulePage.svelte'), props: { id: m[1] }, source: 'src/ModulePage.svelte' };
}}
/>
Relocate or disable an editor route with the editorRoutes prop: <LiveTokensRouter pages={…} editorRoutes={{ editor: '/admin/editor', components: false }} />. A string moves the route; false removes it, along with its nav-rail entry.
The whole overlay surface is dev-only and tree-shakes out of production builds. No {#if import.meta.env.DEV} guards needed.
<script lang="ts">
import Button from '@motion-proto/live-tokens/components/Button.svelte';
import Callout from '@motion-proto/live-tokens/components/Callout.svelte';
</script>
<Callout variant="info">Read this.</Callout>
<Button variant="primary">Save</Button>
Each component carries its own design-token aliases and picks up your tokens.css values automatically. Import only the ones you use.
The editor pages load their own chrome (ui-editor.css, ui-form-controls.css) and the icon font. The only stylesheet you need is a tokens.css declaring the design-token CSS variables on :root.
import '@motion-proto/live-tokens/app/tokens.css';
import '@motion-proto/live-tokens/app/site.css'; // optional: themed h1/p/a styles
import '@motion-proto/live-tokens/app/fonts.css'; // optional: Fraunces + Manrope @font-face
Or copy node_modules/@motion-proto/live-tokens/src/system/styles/tokens.css into your project and edit it. It stays hand-authored: what you Adopt lands in the sidecar tokens.generated.css, never back in tokens.css.
bootLiveTokens and <LiveTokensRouter> are wrappers. The individual init functions (initCssVarSync, initRouter, initColumnsOverlay, initEditorStore, initializeTheme), <LiveEditorOverlay>, <ColumnsOverlay>, and the editor page exports (@motion-proto/live-tokens/editor, @motion-proto/live-tokens/component-editor-page) are all exported. Use them to build a custom shell: arbitrary markup per route, a foreign matcher, or your own overlay wiring. Dynamic and gated routes do not need this; use resolve above, which keeps the overlay, nav rail, and page source intact.
The plugin reads and writes under one folder, src/live-tokens/data/, which holds four subdirectories it owns: colors-and-type/, themes/, component-configs/, and sketch-styles/.
themes/ holds one file per whole look, plus _active.json naming the one the editor has open and _production.json naming the one your site ships. colors-and-type/ and component-configs/{comp}/ hold each layer's default.json baseline, any preset you save by name, and the _working.json buffer for edits you have not saved into the active theme. A buffer is a delta from the open theme, so ordinary theme switching leaves none behind. sketch-styles/ holds one file per look, shipped and your own alike.
To move the data, create live-tokens.config.json at your project root:
{
"dataDir": "src/live-tokens/data"
}
All four keys are optional. dataDir relocates all three subfolders at once. The per-folder overrides cover unusual layouts, such as a monorepo where colors and type are shared across packages but component configs are not:
{
"dataDir": "src/live-tokens/data",
"colorsAndTypeDir": "../shared/colors-and-type",
"componentConfigsDir": "src/live-tokens/data/component-configs",
"themesDir": "src/live-tokens/data/themes"
}
Resolution order, per folder: an explicit themeFileApi(opts) argument, then the matching key in live-tokens.config.json, then <dataDir>/<sub>. The dev server reads the file once at startup, so restart Vite to pick up changes.
npx @motion-proto/live-tokens create my-app
cd my-app
npm install
npm run dev
This generates a Svelte + Vite app that depends on the package, with vite.config.ts, main.ts, App.svelte, the themeFileApi plugin, and a placeholder src/pages/Home.svelte already wired. The token CSS is seeded from the version you scaffolded against. Open http://localhost:5173, replace Home.svelte with your content, and upgrade later with npm update.
create scaffolds this layout. Conforming to it by hand keeps upgrades non-destructive and projects consistent.
src/
main.ts # token CSS chain, then bootLiveTokens(App, '#app')
App.svelte # routes (e.g. <LiveTokensRouter {pages} />)
pages/ # your pages
styles/site.css # your themed page typography (yours to edit)
system/styles/tokens.css # vendored Layer-1 tokens, committed
live-tokens/data/ # editor state, committed
tokens.generated.css # editor output
themes/ # one file per whole look, plus the two pointers
colors-and-type/ component-configs/
vite.config.ts # svelte({ preprocess: vitePreprocess() }) + themeFileApi
svelte.config.js # vitePreprocess()
Conventions that make it work:
tokens.css into src/ and commit it. Point themeFileApi({ tokensCssPath }) at that file, never at one inside node_modules, which npm install wipes.src/ and commit it: tokens.css, tokens.generated.css, and everything in live-tokens/data/. This invariant is what makes upgrades safe, since npm install only touches node_modules, package.json, and the lockfile.vitePreprocess() (bundled in @sveltejs/vite-plugin-svelte) and keep sass installed for the components' SCSS. No svelte-preprocess, no legacy-peer-deps.@motion-proto/live-tokens, /components/*, /vite-plugin, /app/*.The least a consumer needs after npm install @motion-proto/live-tokens:
// src/main.ts
import '@motion-proto/live-tokens/app/tokens.css';
import { mount } from 'svelte';
import App from './App.svelte';
mount(App, { target: document.getElementById('app')! });
<!-- src/App.svelte -->
<script lang="ts">
import Editor from '@motion-proto/live-tokens/editor';
</script>
<Editor />
// vite.config.ts
import { defineConfig } from 'vite';
import { svelte, vitePreprocess } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [svelte({ preprocess: vitePreprocess() })],
});
vite build works as-is: no css: 'injected' workaround, no optimizeDeps excludes. Add themeFileApi and bootLiveTokens from the Install section when you want edits persisted to disk.
The shipped components are first-party, but you can author your own and get the same live editing. Co-locate the runtime and editor files in src/components/ or src/system/components/, then pass them to bootLiveTokens:
// src/main.ts
import { bootLiveTokens } from '@motion-proto/live-tokens';
import App from './App.svelte';
import MyWidgetEditor, { allTokens as myWidgetTokens } from './components/MyWidgetEditor.svelte';
bootLiveTokens(App, '#app', {
components: [{
id: 'mywidget',
label: 'My Widget',
icon: 'fas fa-magic',
sourceFile: 'src/components/MyWidget.svelte',
editorComponent: MyWidgetEditor,
schema: myWidgetTokens,
}],
});
bootLiveTokens calls registerComponent for each entry, gated on import.meta.env.DEV so registration tree-shakes out of production. Call registerComponent directly if you need finer control over timing.
The component appears on /live-tokens/components under a CUSTOM group. Token rows, linked-block sharing, per-component config persistence, and reset-to-default behave exactly as they do for the built-in set. Import only from @motion-proto/live-tokens or @motion-proto/live-tokens/component-editor; never deep-import from src/.
Sketch mode redraws the page as if it had been drawn by hand. Every component keeps its own colors, spacing, and corners; what changes is the line it is drawn with. The effect is a layer over your tokens and never touches a value, so switching it off returns every component to exactly what its tokens say.
Seven looks ship with the package. Each is a full set of dials, and each ships as a file under src/live-tokens/data/sketch-styles/.
Open the Sketchstyle view in the editor, move whatever you like, and Save writes your dials to a file. Saving over a shipped look writes a local copy that shadows it; delete the copy and the shipped file returns, the same way presets work.
A theme carries a sketchstyle of its own, so a look travels with the theme that uses it.
The dev server reads the open theme and paints what it carries. A built site has no server to ask, so hand it the field before mounting:
import { seedSketchFromTheme } from '@motion-proto/live-tokens/sketch';
import theme from './live-tokens/data/themes/sketchy.json';
seedSketchFromTheme(theme.sketchSettings);
await bootLiveTokens(App, '#app');
Register your own sketchstyles at boot the way you register components:
const files = import.meta.glob('./live-tokens/data/sketch-styles/*.json', {
eager: true,
import: 'default',
});
await bootLiveTokens(App, '#app', {
sketchStyles: Object.entries(files).map(([path, file]) => {
const id = path.split('/').pop().replace('.json', '');
return { id, label: file.name || id, settings: file.settings };
}),
});
The file's slug is the look's id, so a sketchstyle picked in the editor keeps working once the site is built. create writes this into main.ts already.
sketchStyles is a store holding every sketchstyle on offer, shipped and your own, in one list. Give each row setSketch(style.id). Off is a state of the effect, so add a None row yourself that calls setSketch(null). unsavedSketchStyle adds one more row when the theme carries sketch settings that no sketchstyle file holds, and reads null otherwise.
The layer redraws the shipped components and four classes it reserves for you: sketch-surface for a box, sketch-container for a large one, sketch-chip for a small one, and sketch-rule for a line. Pick by size, not by kind. The class opts an element in but names no colors, so state them yourself with --sketch-fill, --sketch-stroke, and --sketch-radius. The in-app guide at /live-tokens/docs covers every dial and the rules for images and icons.
npx @motion-proto/live-tokens <command>
| Command | What it does |
|---|---|
create <dir> [--force] | Scaffold a new Svelte + Vite app wired up with live-tokens. |
setup-claude [--force] | Install the bundled Claude Code skills into ./.claude/skills/; --force makes the directory match this release. |
components [id] [--json] | List every component the project has, shipped and its own, with the props each takes; with an id, its props, variants, tokens, and defaults. |
tokens [--scale <name>] [--json] | List every design token the project's tokens.css declares, by scale, with its value. |
report [--json] | The project as facts: pending migrations, tokens each component reads, which page renders which component, and both checkers' findings by rule. Always exits 0. |
check-component [id] [--tests] [--update-aria] [--a11y] [--open-theme] | Validate a component's runtime, editor, and registration against the authoring contract; with no id, every component authored under src/system/components. --tests also runs the contract suites, the accessibility scans among them; --update-aria records each scanned component's accessibility tree anew after a deliberate markup change; --a11y runs the accessibility scans and keyboard cases alone; --open-theme tests the theme the editor has open, as saved. |
check-page [paths...] [--tests] | Validate pages against the create-page contract: catalogue components and their props, theme tokens over literals, route wiring. --tests also opens each page in the app and checks its rendered paint, contrast, layout, and accessibility. |
set-colors <base-colors.json> [--dry-run] | Build the theme's whole color identity from 10 OKLCH base colors, enforce AA contrast on the derived text tokens, and write the result to the unsaved colors-and-type buffer. |
set-type <pairing.json> [--dry-run] [--no-verify] | Bind Google Fonts families to the theme's font stacks, verified against the API. |
set-geometry <ops.json> [--dry-run] | Move radius, padding, gap, and border-width aliases along their token scales. |
save-theme <name> [--no-activate] [--dry-run] | Compose the live state into and open it, which clears the unsaved buffers. |
Once installed in a project, the same commands are available as npx live-tokens <command>.
The package bundles eight Claude Code skills. They encode the conventions this README cannot carry in full: which component fits a need, how a page is wired, how one request becomes a whole look, what a valid theme looks like in OKLCH, how two typefaces sit together, how geometry moves along the token scales, how a project is checked against all of that, and how an existing page or component is brought back into line. Each triggers from an ordinary request, so there are no slash commands to learn.
npx @motion-proto/live-tokens setup-claude
This copies every bundled skill into ./.claude/skills/ in the current directory, leaving any that already exist. macOS and Linux only.
Re-run it with --force after upgrading. That makes the directory match the release: each bundled skill is replaced outright, so a reference file the release dropped goes with it, and a live-tokens- skill the release no longer ships is removed. A skill under any other name is the project's own and is never touched. Without --force nothing is deleted and an existing skill is left alone, so a renamed skill keeps shadowing its replacement until a forced run clears it.
A project created with create runs the forced form from postinstall and gitignores the copy, so the skills track the installed version with nothing to commit. To do the same in an existing project:
// package.json
"postinstall": "live-tokens setup-claude --force || exit 0"
# .gitignore
.claude/skills/live-tokens-*/
live-tokens-pick-componentAsk "TabBar or SegmentedControl?", "how do I let someone pick one of four options?", or "what is the difference between a Callout and a Notification?".
The skill runs npx live-tokens components --json and reads each candidate's catalogue entry: the condition that makes it the right component, the conditions that rule it out with the component to use in its place, and its rules of use. The entries hold the decision, so a component you author joins the same query. It answers the question and writes nothing. Read it before authoring anything new.
live-tokens-create-pageAsk for a page, a route, or a screen: "build a pricing page", "add a /settings route", "put a hero at the top of Home".
The skill composes the page from shipped components, styles every value with var(--token-*) (no hex, no pixel literals, so editor changes repaint the page), places content on the column grid via --columns-count, --columns-gutter, --columns-margin, and --columns-max-width at every width, adds the route as a lazy import with a source so the overlay's "Page Source" button works, and imports site.css from the page rather than main.ts so page CSS stays out of the editor routes. It writes your page files and the route entry, and never touches the data tree.
live-tokens-create-themeAsk for a look: "a dark, moody night theme", "a St Patrick's Day theme in green and gold", "make it feel more serious".
A theme is three decisions: color, type, and geometry. This skill reads the request once, states one design direction that fixes all three, and routes a color, a type, and a geometry intent to live-tokens-set-colors, live-tokens-set-type, and live-tokens-set-geometry. Each of them writes its dimension into the unsaved buffers the app already renders. The skill then runs npx live-tokens save-theme "<name>", which turns those buffers into themes/<slug>.json and opens it, and assembles the three reports into one summary.
It carries the anchor index: every feeling, idiom, era, genre, holiday, and season the skills know, placed on the valence, energy, and dominance axes with the direction each implies. An idiom sets constraints and a feeling moves dials inside them, so "cozy brutalist" reads the idiom first. Each contributing skill holds the mechanics for its own dimension, keyed on the same anchor names.
A refinement that names one dimension goes straight to that sibling. This skill takes the ones that span dimensions.
live-tokens-set-colorsAsk for color: "a cooler palette", "warmer", "more contrast", "calmer", "make the ground darker".
The skill translates the color intent into ten OKLCH base colors (Brand, Accent, Special, Canvas, Neutral, Alternate, Info, Success, Warning, Danger) plus a light or dark scheme, then runs npx live-tokens set-colors <base-colors.json>. The CLI assembles the curves, enforces AA contrast on derived text tokens and auto-corrects where it can, writes the result to the unsaved colors-and-type buffer, and prints a contrast report. Exit 1 means the base colors themselves are unworkable, and each failure line names the base color to change.
Most of the skill is the judgment the generator cannot supply: a chroma budget scaled to how much screen area each palette covers, per-role lightness and hue ranges for each scheme, three levels of canvas commitment, gamut guardrails against impossible base colors, harmony modes, and the optional canvas gradient. OKLCH anchors for every named feeling, idiom, and occasion live in a reference file the skill reads on demand.
Color is the one dimension every look fixes, so it runs first and never skips. --dry-run prints the report without writing. Saving the open theme in the editor, or running save-theme, keeps the result; Adopt ships it. A re-run replaces the buffer's whole color state, including palette edits made in the editor since the last run, and carries the type and geometry buffers forward.
live-tokens-set-typeAsk for type: "pair some fonts for this theme", "what font should the headings use?", "make the type more editorial", "something friendlier", "a serif for headings".
The skill chooses the families and runs npx live-tokens set-type <pairing.json>, which binds each one to --font-display, --font-sans, --font-serif, or --font-mono. Every family is checked against the Google Fonts API before it is written, and the URL is built from the weights that family actually has: a range for a variable font, an enumeration for a static one, a bare URL for a single-weight display face. The report names the weights your typography tokens ask for and the family does not carry.
The judgment is the skill's half. It anchors on the body face, because that is most of the words on the page and text faces survive small sizes where display faces do not. It classifies both candidates by form model (dynamic, rational, geometric) and applies the font matrix: two faces sharing a skeleton under different surfaces pair reliably, two faces sharing a surface over different skeletons fight, and two faces far apart on both read as a decision. It also carries the screen test a body face has to pass, a voice table from intent to type, a type anchor per named feeling and idiom, and the Google Fonts superfamilies for when the type should stay quiet.
Scope: type only, and never color. Edits land in the colors-and-type _working.json buffer, so save the open theme to keep them. --dry-run reports without writing.
live-tokens-set-geometryAsk for shape or space: "make the buttons pill shaped", "sharper corners on the cards", "space it out", "tighter", "thinner borders".
The skill turns the phrase into ops (kind of radius, padding, gap, or border-width, with shift: N or set: <token>, optionally scoped to one component id), then runs npx live-tokens set-geometry <ops.json>. The CLI moves each matching alias along its ladder, reads the live config first so "a bit more" compounds, and prints every change and every skip.
It also knows where these edits go wrong: controls run out of room long before containers do, so a global compaction is one step and anything deeper is aimed at named containers; a pill needs more horizontal padding than a square-cornered control, not less; and content insets stop at --space-4, below which a relative "tighter" reports as clamped instead of writing.
Edits land in each affected component's _working.json buffer, which is what the running page reads, so save the open theme in the editor to keep them. --dry-run reports without writing, and the inverse op is the undo.
live-tokens-create-componentAsk for something the catalogue lacks: "author a Rating component", "make my Chip component editable in the editor".
The skill covers the recipe: the runtime .svelte file with its :global(:root) token block, the editor .svelte file exporting allTokens and its variant groups, the registerComponent() call, and the catalogue entry that keeps live-tokens-pick-component current. It carries the naming scheme, the token suffix vocabulary, the state model (component states such as selected and disabled are separate from interaction states such as hover), and the public-imports rule, and points at the shipped Toggle in node_modules as the worked example. Linked siblings, intrinsics, and the fixed-overlay portal rule sit in reference files the skill reads only when a component needs them.
Verify the result:
npx @motion-proto/live-tokens check-component <id>
The validator checks the file layout, the :global(:root) block, the token-suffix vocabulary, the state-before-property rule, the public-imports rule, the registerComponent({ id }) call, and that every default resolves to a design token rather than a literal. Exit code 0 means the static contract is met. themeFileApi runs it, with check-page, at the start of every vite build, so a component or page that opts out of the theme cannot ship by accident. An error stops the build, and warnings print. themeFileApi({ checks: false }) turns the checks off.
live-tokens-check-complianceAsk how things stand, or ask for the code to catch up: "check this project against the design system and fix it", "audit the pricing page", "make the build's design checks pass", "fix the design-system warnings", "replace the hex and pixel values with tokens", "review this before I upgrade".
The skill runs both checkers, which apply every pending additive tokens.css migration and every auto repair themselves and return each remaining finding with its own guidance. The skill groups what remains by rule, largest error group first, makes each repair from its guidance, and re-runs until both checkers exit 0. It reports what --strict adds and clears the warnings when the request covers them. The reply names the fixes the checkers applied, the changes by rule with any visible shift, and each finding left with its reason. It never silences a rule to pass and never adds a token to tokens.css to match a value a page happened to use. A finding the user chooses to keep goes into the config entry its exception field names.
/live-tokens/editor, /live-tokens/colors, or /live-tokens/components. Edits sit in the working buffer (_working.json). Save in the Theme panel captures the buffer into the open theme at <dataDir>/themes/{name}.json.tokens.generated.css next to your authored tokens.css. Nothing else writes that file, so trying a look never changes what you ship.npm run build bundles both, with no editor code, JSON lookups, or dev surfaces. themeFileApi then rewrites the emitted CSS: it removes each token no rule reads, writes each token whose value never changes as its value, and keeps a variable for the rest. Your source files keep every token name. The user guide's Production CSS chapter covers the rule, the keep option, and the limits.Knowing which files the plugin touches matters when you upgrade the package or work in a repo you do not want overwritten. For how a saved look stays safe across upgrades while tokens.css holds the building blocks, see TOKENS.md.
On npm install or npm update: nothing outside node_modules/. There are no install hooks. Upgrading never touches src/live-tokens/data/ or any other file in src/.
The plugin writes in two places only:
src/live-tokens/data/, configurable through live-tokens.config.json.tokensCssPath: tokens.generated.css and fonts.css.At dev-server startup it fills gaps and refreshes its own derived files, and overwrites no authored file:
<dataDir>/themes/default.json, the derived Default theme, regenerated when the shipped colors and type or a component default changes.<dataDir>/themes/_active.json and _production.json, written only when missing, and healed when they name a theme that no longer resolves.<dataDir>/component-configs/{comp}/default.json, regenerated from the component's :global(:root) block only when the .svelte source is newer than the existing file. It is a build artifact of the source, so do not hand-edit it.tokens.generated.css beside your tokensCssPath, rebaked from the production theme so a fresh checkout builds against the look you shipped.Editor actions rewrite these:
<dataDir>/colors-and-type/_working.json and <dataDir>/component-configs/{comp}/_working.json, the buffers, written as you edit and cleared when a theme you open does not carry them.<dataDir>/themes/{name}.json, on every Save and Save As in the Theme panel.<dataDir>/colors-and-type/{name}.json and <dataDir>/component-configs/{comp}/{name}.json, only when you save a preset by name.<dataDir>/sketch-styles/{name}.json, on every Save and Save As in the Sketchstyle view. Saving over a shipped look writes this project's own copy under the same name; deleting it restores the shipped file.tokens.generated.css and fonts.css, regenerated from the production theme when you Adopt.The plugin never writes your authored tokens.css. It holds defaults you are free to hand-edit, and the editor's overrides land in tokens.generated.css, which the app imports immediately after it.
The one exception is themeFileApi({ autoMigrate: true }). With it enabled, the dev server applies pending additive token migrations (new token names only) to tokens.css at startup and writes the file, so it keeps up with the package as you upgrade. The change shows up in git for review. Breaking migrations that rename or remove tokens are never applied automatically; run npx live-tokens migrate for those during a deliberate upgrade. The option is off by default. See TOKENS.md.
MIT. Originally extracted from RuneGoblin.
themes/<slug>.jsonmigrate [--check] [--write] [--tokens <path>] | Reconcile the project with the installed package: every tokens.css migration, breaking ones included, the pre-0.48 data-tree move, and a report on source references to the routes that moved in 0.35.0. |