Point it at any static site and a theme.css — get a live-knob design dashboard in your browser, preview against the real site, ship through your own git. No accounts, no lock-in, no copyright.
Zero npm dependencies. One server file, one HTML file. Nothing phones home — the only outbound call is the Bunny Fonts catalogue, and only when you open the font browser.
In the root of a site repo:
npx theme-studio init # scans your CSS, drafts theme/theme.css + studio.config.json
npx theme-studio # opens the dashboard on http://localhost:4400Already have a theme.css in the token convention? Skip init, write the
config by hand:
{
"themeFile": "theme/theme.css",
"sites": [
{ "name": "My site", "preview": "./dist" },
{ "name": "Live", "preview": "https://example.com" }
],
"ship": { "mode": "git", "paths": ["theme"], "branch": "main" },
"uploads": "public/",
"fonts": true
}That's the whole schema. preview takes a built folder (served locally), a
live URL, or a dev-server URL — URLs are proxied with a small edit bridge
injected, so the knobs work on the real thing. ship.mode is "git" for
the Ship button (add, commit, pull --rebase, push — never force) or
"none" for save-only. uploads is where font files land. fonts: false
hides the font tools entirely.
Every token in your theme file becomes a control, grouped the way your file is grouped: colour pickers with live WCAG contrast badges, px/rem/em sliders with a ≈px readout, weight selects, show/hide switches, font-stack pickers. Changes inject into the preview instantly — no rebuild, no reload. A snippets box takes any CSS the knobs don't cover. Light/dark toggle, desktop/tablet/mobile viewports, search filter, drag-to-rearrange control boxes (saved server-side, same on every device), export/import of all settings as one JSON file, and a defaults snapshot to reset to.
Fonts: browse the Bunny Fonts catalogue (a no-tracking Google Fonts mirror), type a Google Fonts name, or upload a file. Web-font links preview only; Self-host downloads just the latin woff2 files into your uploads folder and writes the @font-face rules into your theme file — your fonts ship with your site, no third-party requests.
Theme Studio reads one CSS file. The rules:
:root {
/* @group Colours */
--page-bg: #ffffff; /* Page background */
--page-text: #222222; /* Body text */
/* @group Header */
--display-header-cta: inline-flex; /* Header button [on=inline-flex] */
}
.dark {
--page-bg: #111111;
}
/* THEME-STUDIO:SNIPPETS:START */
/* THEME-STUDIO:SNIPPETS:END */- Light values live in
:root; dark overrides live in an optional.darkblock (your site toggles a.darkclass on<html>). Tokens without a dark override are shared between both themes. - A
/* @group Name */comment starts a control group — that's a sidebar box in the dashboard. - The trailing comment after each token is its control label.
- Tokens named
--display-*render as show/hide switches; the[on=value]hint in the label says what "shown" means (defaultblock). - Name colour pairs
--thing-text/--thing-bgand the dashboard badges their WCAG contrast automatically.--link,--link-hoverand--mutedare judged against--page-bg. - The snippet markers are where the snippets box writes.
initadds them; the studio adds them on first save if they're missing. - One hard rule: never nest
/* */inside a comment — it terminates the comment early and takes your:rootblock with it.
Your site consumes the tokens the normal way: link the theme file before
your other CSS and write var(--page-bg) wherever you'd have hardcoded a
value. Plain CSS custom properties — no build step, fully portable, and the
file stays hand-editable.
There is no auth, by design. The studio binds to 127.0.0.1 and that's the
security model. To use it from your phone or another machine, bind to a
private network — Tailscale or similar — with STUDIO_HOST=0.0.0.0 (or the
private interface's IP), and never port-forward it to the open internet.
Environment knobs: STUDIO_PORT (base port, default 4400 — previews take
the next ports up), STUDIO_HOST, STUDIO_CONFIG (config path),
BUNNY_LIST_URL (override the fonts catalogue source, for offline use or
testing).
Needs Node 18 or newer. The .theme-studio/ folder holds your box-order
preferences, font list and defaults snapshot — gitignore it or commit it,
either works.
- Documents → PDF — a
"document"target type: an HTML template plus print tokens (page size, margins, headers); preview is the rendered document; export via the browser's print-to-PDF. Live-knob letterheads, CVs, one-pagers. - Content panels via schema — a
content.schema.jsondescribing fields that renders its own editing panel. - CMS adapters — preview already works on any URL; persistence needs per-platform write adapters (WordPress first).
- Tailwind-aware
init— v1 scans plain CSS only.
I call this the Freehold Licence: you own your copy outright — no landlord, no licence fees, no asking. Copy it, change it, sell it, ship it as your own; no credit, no notices, no CLA. Legally it is CC0 1.0 Universal — LICENSE is the licence, unmodified, and the only legal instrument here.