@motion-proto/live-tokens
v0.68.1
Published
Design token editor with live CSS variable editing. Svelte 5 + Vite 8.
Maintainers
Readme
LiveTokens
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-tokensThe editor is dev-only. Production builds get plain CSS variables and the components you used.
Features
- Live token editing. Colors, typography, spacing, radii, shadows, motion, palettes, and gradients. Every input writes a CSS variable, so the page repaints with no reload and no build step.
- Live component editing. 25 shipped Svelte components (Button, IconButton, Input, Card, Dialog, Badge, Callout, Table, Tooltip, Toggle, TabBar, SegmentedControl, RadioButton, MenuSelect, ProgressBar, CornerBadge, SectionDivider, CollapsibleSection, Notification, Image, ImageLightbox, CodeSnippet, SideNavigation, Panel, InlineEditActions) declare their design-token aliases in a
:global(:root)block. Rewire an alias from the component's editor and it updates everywhere that component is used, on your real pages. - Four dev-only routes.
/live-tokens/editorfor tokens,/live-tokens/colorsfor palettes,/live-tokens/componentsfor per-component aliases,/live-tokens/docsfor the user guide. - Editor overlay. Pins to the top right of every dev page and opens the editor in a side panel or floating window, so you edit on the page you are styling. Its "Page Source" button opens the current page's
.sveltefile in VS Code. - Themes. A theme is a whole look in one file: colors and type plus a config for every component, stored by value. Loading one changes a single pointer file, and nothing your site ships changes until you Adopt. Export a theme and import it into another project to restore the look in one step.
- Eight example looks. Autumn, Halloween, Midnight Study, Ocean, Royal Velvet, Sketchy, Spring Meadow, and Sunset each ship as a full theme: colors, a Google Fonts pairing, and a shape personality of radius, padding, gap, and border-width aliases. They ship inside the package, so trying one needs no local files. Load Motion Proto to return to the default. Saving over a preset writes a local copy that shadows the shipped one; delete the copy and the shipped version returns.
- Sketch mode. Redraws the page as if it had been drawn by hand. Seven looks ship as files, and the effect is a layer over your tokens rather than a change to them. See Sketch mode.
- Vite plugin. Hosts the
/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. - Claude Code skills. Six bundled skills that drive the package from plain English. See Claude Code skills.
Install
Vite config
// 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:
- Resolves the
defaultcolors and type from the installed package, so you start on the shipped defaults with no local copy. - Discovers components at
src/components/*.svelteandsrc/system/components/*.svelte, and seedssrc/live-tokens/data/component-configs/{comp}/default.jsonfrom each component's:global(:root)block. - Writes
src/live-tokens/data/themes/default.jsonat 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. - Bakes
tokens.generated.cssfrom the production theme at startup, so a fresh checkout builds against the look you shipped. - Hosts the
/api/live-tokens/*routes the editor saves and loads through. - Injects
__PROJECT_ROOT__for the overlay's "Page Source" link and__LIVE_TOKENS_API_BASE__so the client uses whateverapiBaseyou 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.
Bootstrap in 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.
Mount routes with <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.
Use components
<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.
Styles
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-faceOr 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.
Lower-level API
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 data directory
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.
Scaffold a new app
npx @motion-proto/live-tokens create my-app
cd my-app
npm install
npm run devThis 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.
Recommended project layout
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:
- Vendor
tokens.cssintosrc/and commit it. PointthemeFileApi({ tokensCssPath })at that file, never at one insidenode_modules, whichnpm installwipes. - Keep all editable state under
src/and commit it:tokens.css,tokens.generated.css, and everything inlive-tokens/data/. This invariant is what makes upgrades safe, sincenpm installonly touchesnode_modules,package.json, and the lockfile. - Nothing is backed up for you. The dev server keeps no snapshots, so git is the safety net. Commit a theme you care about before editing over it.
- Preprocess with
vitePreprocess()(bundled in@sveltejs/vite-plugin-svelte) and keepsassinstalled for the components' SCSS. Nosvelte-preprocess, nolegacy-peer-deps. - Import only from the public surface:
@motion-proto/live-tokens,/components/*,/vite-plugin,/app/*.
Minimal setup without the plugin
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.
Consumer-authored components
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
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/.
- Pencil. Two graphite passes on their own seeds, so the outline disagrees with itself the way a hand coming back round does. The grain is drawn long and on the diagonal, the way a pencil shades.
- Marker. Broad translucent nib gone round twice on the same line, so the overlap darkens and the ink pools where it slows.
- Whiteboard. The fattest nib on glass. One long smooth undulation, and a veined mask that streaks the fill like a half-wiped board.
- Hatched. An etching. The fill is angled shading, the outline a single hard-edged scratch that chatters along its length. No mask: the hatch is the texture.
- Dashed. A drafting outline. One slow drift along the ruler, broken into strokes, with jitter, mask and pressure all off. The clean pole.
- Napkin. Ballpoint in a hurry. Everything loose at once: a square wave sends every edge to full travel, and the second pass lands wherever it lands.
- Dry marker. Ink that ran out. One scratchy pass that breaks up along its length, over a fill the mask has worn nearly through in patches.
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.
Ship the layer with your site
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.sketchStyle);
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', {
sketchLooks: 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.
Build a picker
sketchLooks is a store holding every look on offer, shipped and your own in one list. Give each row setSketch(look.id), and add a None row yourself: off is a state of the effect rather than one of the looks. themeSketchLook carries the theme's own look as one more row, and reads null when the theme carries none.
Draw your own elements
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.
CLI
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/. |
| check-component <id> | Validate a component's runtime, editor, and registration against the authoring contract. |
| generate-theme <brief.json> [--no-activate] [--dry-run] [--carry-from <name>] | Build a full theme from a 10-seed OKLCH brief, enforce AA contrast, write themes/<slug>.json, and open it. |
| adjust <ops.json> [--dry-run] | Move radius, padding, gap, and border-width aliases along their token scales. |
| set-fonts <brief.json> [--dry-run] [--no-verify] | Bind Google Fonts families to the theme's font stacks, verified against the API. |
| migrate [--check] [--write] [--tokens <path>] | Reconcile the project with the installed package: additive tokens.css migrations, the pre-0.48 data-tree move, and a report on source references to the routes that moved in 0.35.0. |
Once installed in a project, the same commands are available as npx live-tokens <command>.
Claude Code skills
The package bundles six Claude Code skills. They encode the conventions this README cannot carry in full: which component fits a need, how a page is wired, what a valid theme looks like in OKLCH, how two typefaces sit together, and how geometry moves along the token scales. Each triggers from an ordinary request, so there are no slash commands to learn.
Install
npx @motion-proto/live-tokens setup-claudeThis copies every bundled skill into ./.claude/skills/ in the current directory. Re-run it after upgrading the package to pick up new and changed skills, adding --force to overwrite. macOS and Linux only. The equivalent by hand:
mkdir -p .claude/skills && cp -R node_modules/@motion-proto/live-tokens/.claude/skills/. .claude/skills/live-tokens-pick-component
Ask "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 holds the catalogue grouped by job (action, input, selection, containers, messaging, display) and a decision table for each confusable family: SegmentedControl vs TabBar vs RadioButton vs MenuSelect, Card vs CollapsibleSection vs Dialog, Callout vs Notification vs Tooltip vs Badge, Button vs IconButton, and the on/off case. It answers the question and writes nothing. Read it before authoring anything new.
live-tokens-build-page
Ask 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, and --columns-max-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-generate-theme
Ask for a look: "a dark, moody night theme", "a St Patrick's Day theme in green and gold", "warmer", "more contrast", "calmer".
A theme is three decisions made from one brief: color, type, and geometry. The skill owns color and delegates the other two to live-tokens-pair-fonts and live-tokens-adjust-geometry, so the whole look comes from the same reading of the brief.
For color it translates the brief into ten OKLCH seeds (Brand, Accent, Special, Canvas, Neutral, Alternate, Info, Success, Warning, Danger) plus a light or dark scheme, then runs npx live-tokens generate-theme <brief.json>. The CLI assembles the curves, enforces AA contrast on derived text tokens and auto-corrects where it can, writes themes/<slug>.json, opens it, and prints a contrast report. Exit 1 means the seeds themselves are unworkable, and each failure line names the seed 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 bands for each scheme, gamut guardrails against impossible seeds, harmony modes, the optional canvas gradient, and a voice-to-shape table for the shape step. OKLCH anchors for named holidays and seasons live in a reference file the skill reads on demand.
Color lands in the theme file; type and shape land in the unsaved buffers, and one Save keeps all three. --dry-run prints the report without writing; --no-activate writes without opening. Opening a theme never changes what your site ships; Adopt does. Regenerating replaces that theme's whole color state, including palette edits made in the editor since the last run, and carries the live buffers forward, so re-rolling color after setting fonts and shape keeps both.
live-tokens-pair-fonts
Ask 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-fonts <brief.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 brief to type, 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-adjust-geometry
Ask 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 adjust <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-component
Ask 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 no-raw-color-defaults rule, the public-imports rule, and the registerComponent({ id }) call. Exit code 0 means the static contract is met. Use it after Claude generates a component, and as a pre-commit guard on hand-authored ones.
From edit to production
- Edit on
/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. - Adopt the theme. It becomes the production theme, and its variables are baked into
tokens.generated.cssnext to your authoredtokens.css. Nothing else writes that file, so trying a look never changes what you ship. npm run buildbundles both as plain CSS. No editor code, no JSON lookups, no dev surfaces reach production.
File ownership
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 throughlive-tokens.config.json.- The CSS sidecars next to your
tokensCssPath:tokens.generated.cssandfonts.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.jsonand_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.sveltesource is newer than the existing file. It is a build artifact of the source, so do not hand-edit it.tokens.generated.cssbeside yourtokensCssPath, rebaked from the production theme so a fresh checkout builds against the look you shipped.
Editor actions rewrite these:
<dataDir>/colors-and-type/_working.jsonand<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}.jsonand<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.cssandfonts.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.
License
MIT. Originally extracted from RuneGoblin.
