A Svelte + Vite design-token system with an in-browser editor. Ships two ways:
Home.svelte with your app, edit tokens live.npm install @motion-proto/live-tokens into an existing Svelte app and import the overlay + editor.Both modes are supported and maintained together.
/editor (dev-only). Edit colors, typography, spacing, radii, shadows, and motion; the whole site updates live.vscode:// link)./components: Button, Card, Dialog, Badge, Tabs, Tooltip, Toggle, and more. Extendable with your own sections.themes/. The active palette syncs into src/styles/tokens.css on save, so production builds ship pure CSS — no editor code or JSON lookups in the prod bundle.npx degit motionproto/live-tokens my-app
cd my-app
npm install
npm run dev
Open http://localhost:5173. Replace src/pages/Home.svelte with your own landing page. Keep or delete src/pages/Demo.svelte (the /demo route) depending on whether you want the demo around as a reference.
src/pages/Home.svelte — your app's / route. Replace this with your content.src/pages/Demo.svelte — /demo route. Marketing/demo page for the kit itself. Safe to delete once you don't need it.src/pages/Editor.svelte — /editor route. Design-system editor.src/pages/ComponentEditorPage.svelte — /components route. Component editor.src/App.svelte — top-level router + overlay wiring.src/components/ — reusable components.src/component-editor/ — per-component editors plus shared scaffolding.src/ui/ — neutral editor UI primitives and the design-system editor surfaces.src/lib/ — overlay, router, token persistence, color helpers.src/styles/tokens.css — the generated CSS variables (source of truth at runtime).themes/ — persisted theme files. _active.json = theme loaded on dev. _production.json = theme synced to CSS on "promote."/editor → the overlay writes to the active theme file in themes/.src/styles/tokens.css and backed up under src/styles/_backups/.npm run build bundles that CSS file as-is.Install into an existing Svelte 4 + Vite project:
npm install @motion-proto/live-tokens
// vite.config.ts
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { themeFileApi } from '@motion-proto/live-tokens/vite-plugin';
export default defineConfig({
plugins: [
svelte(),
themeFileApi({
themesDir: 'themes',
tokensCssPath: 'src/styles/tokens.css',
}),
],
optimizeDeps: {
exclude: ['@motion-proto/live-tokens'],
},
});
The themeFileApi plugin:
themes/ with a default theme on first dev-server start./api/* routes the editor uses to save/load themes.__PROJECT_ROOT__ so the overlay's "Page Source" link can open files in VS Code. You don't need a define entry for this.main.tsimport { configureEditor, initializeTheme } from '@motion-proto/live-tokens';
import App from './App.svelte';
configureEditor({ storagePrefix: 'my-app-' });
async function boot() {
if (import.meta.env.DEV) {
await initializeTheme();
}
new App({ target: document.getElementById('app')! });
}
boot();
<!-- App.svelte -->
<script lang="ts">
import { LiveEditorOverlay, ColumnsOverlay } from '@motion-proto/live-tokens';
import Editor from '@motion-proto/live-tokens/editor';
import { route } from './router';
</script>
<LiveEditorOverlay
navLinks={[
{ path: '/', label: 'Home', icon: 'fa-home' },
{ path: '/components', label: 'Components', icon: 'fa-puzzle-piece' },
]}
pageSources={{
'/': 'src/pages/Home.svelte',
'/components': 'src/pages/ComponentsPage.svelte',
}}
/>
<ColumnsOverlay />
{#if $route === '/editor'}
<Editor />
{:else}
<!-- your routes -->
{/if}
<LiveEditorOverlay /> self-gates: it only renders in dev, never inside an iframe, and never when the user is on editorPath (defaults to /editor). You don't need to wrap it in {#if import.meta.env.DEV} guards.
// main.ts — or wherever you import global styles
import '@motion-proto/live-tokens/styles/editor.css';
import '@motion-proto/live-tokens/styles/form-controls.css';
import '@motion-proto/live-tokens/styles/fonts.css';
<!-- src/pages/ComponentsPage.svelte -->
<script lang="ts">
import { ComponentsTab, defaultSections, type ComponentSection } from '@motion-proto/live-tokens/component-editor';
import '@motion-proto/live-tokens/styles/editor.css';
import MyWidgetEditor from '../components/MyWidgetEditor.svelte';
const mySections: ComponentSection[] = [
{ id: 'myWidget', label: 'My Widget', component: MyWidgetEditor },
];
const allSections = [...defaultSections, ...mySections];
</script>
<ComponentsTab sections={allSections} />
The package is published to npm as @motion-proto/live-tokens.
package.json.npm pack --dry-run.npm publish --access public — prepublishOnly rebuilds dist-plugin/.git tag v0.2.0 && git push origin main --tags.What ships:
src/lib/, src/ui/, src/component-editor/, src/components/src/pages/Editor.svelte + Editor.svelte.d.tssrc/ui/editor.css, src/styles/form-controls.css, src/styles/fonts.css, src/styles/fonts/dist-plugin/ — compiled Vite pluginWhat doesn't ship (starter-only): src/App.svelte, src/main.ts, src/pages/Home.svelte, src/pages/Demo.svelte, src/pages/ComponentEditorPage.svelte, index.html, themes/.
MIT. Originally extracted from RuneGoblin.