Theming & chrome

Chrome (packages/shell/src/shell/ui/chrome.ts) is an eight-axis design system: mode, design, accent, font, radius, density, animation, layout. Every axis has a per-app default and a live picker in the design panel. Swap a key and the whole app re-skins with no rebuild.

How designs are applied

applyRoot writes the state onto document.documentElement: data attributes data-mode, data-design, data-font, data-radius, data-density, data-animation, plus two inline CSS variables for the accent — --lab-accent (the color) and --lab-accent-contrast (its readable text color). The font axis sets --lab-font from the chosen stack; the special font "auto" removes the inline --lab-font so the active design's own signature typeface shows through. Each design name maps to a [data-design="name"] block in the shell stylesheet that restyles every surface via the token contract; the other axes overlay on top of it.

The 14 design categories (100 designs)

  • Minimal (8) — flat, minimal, swiss, scandinavian, muji, hairline, monochrome, ultraflat
  • Soft (6) — soft, neu, claymorphism, pillow, marshmallow, aerogel
  • Glass (5) — glass, frosted, crystal, auroraglass, liquidglass
  • Brutalist (7) — brutalist, neobrutalist, concrete, industrial, raw, punk, xerox
  • Retro (12) — y2k, vaporwave, synthwave, retrowave, pixel, arcade, win95, geocities, cassette, atompunk, midcentury, atomic
  • Deco (8) — artdeco, bauhaus, memphis, constructivist, mondrian, destijl, artnouveau, opart
  • Futuristic (10) — cyberpunk, neon, tron, hologram, scifihud, matrix, glitch, circuit, quantum, hud
  • Editorial (8) — newspaper, magazine, editorial, typographic, book, zine, broadsheet, manuscript
  • Organic (10) — botanical, earthy, watercolor, sketch, papercraft, origami, ceramic, wabisabi, forest, ocean
  • Luxe (7) — darkluxe, goldfoil, marble, velvet, couture, obsidian, platinum
  • Playful (8) — candy, bubblegum, popart, sticker, doodle, kawaii, crayon, comic
  • Corporate (5) — corporate, saas, dashboard, fintech, clinical
  • Gradient (4) — gradient, sunset, iridescent, prism
  • Dark (2) — midnight, noir

The 18 accents

blue, violet, indigo, cyan, teal, green, lime, amber, orange, rose, red, magenta, purple, sky, emerald, gold, slate, coral. Each Accent carries { name, label, value, contrast } — value becomes --lab-accent, contrast becomes --lab-accent-contrast. Other axes: 11 fonts (auto + 10 stacks), 4 radii (sharp/soft/round/pill → --lab-radius), 4 densities (compact/cozy/comfortable/spacious), 3 animation levels (none/subtle/wild), 3 marketing layouts (centered/split/editorial).

The API

initDesign(defaults?: Partial<DesignState>): void   // called once by createLabApp
currentDesign(): DesignState                        // app defaults + saved overrides
setDesign(patch: Partial<DesignState>): DesignState // change axes, persist just those
applyDesign(state: DesignState, persist = true): void
resetDesign(): DesignState                          // clear overrides, back to defaults
designDefaults(): DesignState
randomDesign(): Partial<DesignState>                // panel "shuffle"

Persistence is a partial: only axes the user explicitly overrode are stored (localStorage key geenius-mvp.design.v4; stale v1-v3 keys are cleared on init). Every other axis resolves through the app's current default, so changing an app default later still reaches returning visitors on untouched axes. Built-in fallback defaults: dark / flat / blue / sans / soft / cozy / subtle / centered.

Per-app overrides: theme.css

Every scaffolded app ships src/theme.css, imported in main.tsx AFTER the shell stylesheet — so anything you put there wins. Override chrome variables or add brand CSS without touching the shell package:

/* src/theme.css */
:root { --lab-accent: #ff6a00; --lab-radius: 4px; }
Precedence gotchas: setDesign writes --lab-accent as an inline style on the html element, which beats a plain :root rule in theme.css — so a visitor's saved accent override wins over your CSS. To hard-pin a value against the picker, scope it with higher specificity (or !important), or better, set the app's default via initDesign/createLabApp instead of CSS. Also remember the catalogs live ONLY in chrome.ts — the scaffolder parses that file; never hardcode a design list elsewhere.

App layouts

Separately from chrome, authed routes pick one of 4 slot-based layouts (app-layouts.tsx): sidebar (classic left nav, default), header (horizontal top nav), windows (bottom taskbar + start menu), macos (top menu bar + dock). All receive the same slots — brand, nav, workspace, account, content — and are responsive below 720px. Chosen via appLayout in createLabApp or --layout at scaffold time.