Theme Engine

Lucian Labs system A — a contract for what a look is. Shipped in Forget Me Not.

Not a look — a contract

Console, Punch Clock and House Pour each commit to one identity. This does the opposite: it defines the shape a look must have, then ships eleven of them. Reach for it when the app must be re-skinnable — by the user, by a URL, or by you at build time.

type ThemeStyle = {
  name, label
  colors: { bg surface border text dim   // structure
            accent                        // identity — one hue
            green orange red cyan }       // state — never repurposed
  borderRadius, fontSize, spacing         // form
  headerFont, bodyFont, fontFamily        // voice
  animation                               // motion signature
  sound: { preset, bpm, volume, mode }    // notification character
}

Ten colour roles, one radius, two faces, one density, one motion, one sound. That is the entire surface area. If a component needs a colour outside the ten roles, the component is wrong — not the theme.

Eleven presets — click to swap

Every value in the panel below is a CSS custom property. Switching rewrites the properties; nothing re-renders.

Midnight
radius 6px · 14px · Fira Code / Fira Code · normal · fade
Laundry in the washer
every 2 days · due in 4h
Water the monstera
weekly · fresh
Bins out
weekly · overdue 2d

Tap a ✓ to play this theme's exit animation. Enter is the same keyframes reversed and faster.

Motion is part of the theme

This is the idea worth stealing. Each theme names one exit animation, and it carries as much identity as the palette.

Midnightfadeopacity out, scale to .95 — 0.4s ease-out
Sunrisefloatrises 40px and fades — 0.5s ease-in-out
Selvagrowswells to 1.1 and blurs 4px — 0.5s ease-out
Kenteslidefeints right, then hard left off-screen — sharp cubic-bezier
Neonglitch7 stepped frames, hue-rotates 720°, clip-path tears, collapses scaleY
Clouddriftup 20px, right 30px, 2px blur — 0.6s ease-in-out
Terracottacrumblesettles 4px, then falls 30px rotating 2° — 0.5s ease-in
Matchazen8px blur + brightness lift, barely moves — 0.7s ease-out
Vinylspin180° rotation shrinking to .3 — 0.5s
Océanowaverides up, down, then away on scaleX — 0.6s ease-in-out
Sakurapetals5 frames drifting up-and-sideways, rotating 8° — 0.7s ease-out

Four rules that make it cohere

Durations sit between 0.4s and 0.7s — long enough to read as character, short enough to stay out of the way.

Application

Build your own

Honest caveat

These presets are a user-facing customization system, not the house brand. Several use faces the house ban list rejects for brand work (Poppins, Inter, Nunito) — fine here, because the point of this system is range, not identity.

Two things are still wrong: midnight and neon fall back to 'SF Mono', which the house rule forbids outright — that should be Inconsolata. And if you're building a Lucian Labs product rather than a re-skinnable toy, use Console, Punch Clock or House Pour and take only the architecture from this page.