Lumeo

Theming with tweakcn / shadcn

tweakcn is a visual theme editor for the shadcn/ui ecosystem. Lumeo's color tokens are natively compatible with it: paste a shadcn / tweakcn :root / .dark export and the whole library re-skins — no adapter, no import, no renaming.

TL;DR

  • Build a theme at tweakcn, export the Tailwind v4 / CSS-variables form (OKLCH).
  • Paste its :root / .dark blocks into your app, loaded after lumeo.css.
  • Done. Colors, --radius, spacing, tracking, fonts and shadows all drive Lumeo 1:1 — Tailwind v4 reads them as variables. Only Lumeo's own status colors have no tweakcn source (see below).

How it works

shadcn / tweakcn write unprefixed variables (--primary); Lumeo's components read the Tailwind v4 --color-* namespace. Lumeo maps the two natively at the source — every token is defined as the shadcn variable with Lumeo's own value as the fallback (no separate adapter or import file involved):

/* lumeo.css — every color token maps to the shadcn name (built in, no import) */
:root {
    --color-primary:    var(--primary,    oklch(0.21 0.006 286));
    --color-background: var(--background, oklch(1 0 0));
    --color-border:     var(--border,     oklch(0.92 0.004 286));
    --color-ring:       var(--ring,       oklch(0.21 0.006 286));
    /* …every color token, base + dark + all palettes… */
    --radius: 0.75rem;   /* same name as shadcn — nothing to map */
}

So if you set --primary, it flows straight into --color-primary and every bg-primary / button / ring picks it up. If you don't, the fallback keeps Lumeo pixel-identical to its defaults — including dark mode and the eight built-in palettes. --radius needs no mapping at all: both projects already call it --radius.

Setup

Tailwind v4

@import "./_content/Lumeo/css/lumeo.css" layer(base);

/* paste your tweakcn / shadcn export below — that's it */
:root { --primary: oklch(0.205 0 0); --radius: 0.625rem; /* … */ }
.dark  { --primary: oklch(0.985 0 0); /* … */ }

Without Tailwind

<link href="_content/Lumeo/css/lumeo.css" rel="stylesheet" />
<style>
    :root { --primary: oklch(0.205 0 0); /* tweakcn light vars */ }
    .dark { --primary: oklch(0.985 0 0); /* tweakcn dark vars  */ }
</style>

Load order matters: your shadcn variables must come after lumeo.css so they win the cascade. You can ignore the @import "tailwindcss", @theme inline and @layer base lines in the tweakcn export — Lumeo already provides those.

What carries over — and what doesn't

Token group Carries over Notes
All colors
Yes
background, foreground, card, popover, primary, secondary, muted, accent, destructive (+ foreground), border, input, ring, chart-1…5, and the full sidebar family — all map 1:1.
--radius
Yes
Same name in both. Lumeo derives its own --radius-sm/md/lg/xl from it, so set only the master --radius (don't paste tweakcn's derived scale).
Spacing & tracking
Yes
Tailwind v4 compiles Lumeo's spacing utilities to calc(var(--spacing) * n) and tracking to letter-spacing: var(--tracking-*), so a pasted --spacing / --tracking-* rescales the whole library. (Verified: doubling --spacing doubles every padding/margin/gap.)
Fonts
Yes *
Lumeo never hard-codes a font — components inherit, and the font-sans / font-mono utilities resolve var(--font-sans) / var(--font-mono). * Apply the font on your <body> (every tweakcn export does) and it cascades into Lumeo.
Shadows
Yes
Lumeo maps the shadow-* utilities to var(--shadow-*) (overriding only --tw-shadow, so focus-ring composition stays intact), with defaults mirroring Tailwind's scale. A pasted --shadow-2xs--shadow-2xl drives every Lumeo elevation 1:1.
Lumeo status colors
No
tweakcn never emits info / success / warning / positive, so they keep Lumeo's defaults. Override the --color-* directly if a recolored palette clashes.

Demo: a shadcn palette driving Lumeo

This block sets the bare shadcn names (--primary / --ring) on one subtree — exactly what a pasted tweakcn export does globally — and Lumeo picks up the teal accent with no extra wiring.

shadcn accent

Driven by a bare --primary, natively.

See also