A hundred designs, one CSS-variable contract

Every scaffolded app ships with 100 visual designs. Not 100 color palettes — 100 distinct visual languages, from Swiss minimalism to neo-brutalism to a full Windows 95 homage, grouped into 15 families: Minimal, Soft, Glass, Brutalist, Retro, Deco, Futuristic, Corporate, Dark, Editorial, Gradient, Layout, Luxe, Organic, and Playful. Cross that with 18 accents and dark and light modes and you have thousands of looks from one codebase.

One app, many skins — same DOM, different variables.

The contract

The mechanism is deliberately boring: a token contract of CSS variables. Every design is a [data-design="name"] block that reassigns those variables — surfaces, borders, radii, shadows, typography. Setting one attribute on the root restyles the entire app. No component re-renders, no theme provider, no JS in the hot path. The accent is a second axis: --lab-accent plus a contrast pair, and surfaces tint toward it. Mode flips luminance on top. The axes are independent, which is what makes 100 x 18 x 2 tractable — you write 100 designs, not 3,600.

In the catalog, a design is just a name, a label, and a family:

{ name: "glass", label: "Glass", category: "Glass" },
{ name: "frosted", label: "Frosted", category: "Glass" },
{ name: "crystal", label: "Crystal", category: "Glass" },
{ name: "auroraglass", label: "Aurora Glass", category: "Glass" },
{ name: "liquidglass", label: "Liquid Glass", category: "Glass" }

That catalog in the shell's chrome.ts is the single source of truth. The scaffolder's --list --json manifest is parsed out of it, so the CLI can never advertise a design that does not exist.

Why everything themes automatically

The rule that makes this work: no component ships a hardcoded color. Every shell primitive — cards, buttons, nav, modals, toasts, the auth screens — is written against the variable contract. So are the nine entity views. When you switch from muji to cyberpunk, the kanban board, the calendar, the stats tiles, and the draggable canvas all follow, because they never knew what color they were in the first place. Feature code you write inherits the same deal for free as long as it uses the shell's primitives and variables.

Pick at scaffold time, change at runtime

pnpm new my-idea --type webapp --design prism --accent violet --mode dark

The flags are validated against the catalog — a typo gets you the list of valid names, not a broken app. And nothing is locked in: every app ships an in-app design panel where users flip design, accent, and mode live. It is also the honest test of the architecture. A theme switcher that can hit 100 designs at runtime cannot be faking it with per-theme components; there is only one DOM and one contract.

You write 100 designs, not 3,600 — because the axes never meet in code.

For an agent building on top, the takeaway is simple: never invent a color. Use the variables, and whatever you build belongs to all hundred designs at once.

← All posts