Theme Overrides
Lumeo's theme system is a layered set of CSS custom properties. The library ships seven
built-in themes (Zinc, Blue, Green, Rose, Violet, Amber, Teal) in both light and dark mode.
You override the theme at three levels: globally with CSS variables, per-mode with dark-mode
swaps, and per-component with the Class
parameter. Every Lumeo component reads colors from variables — there are no hard-coded hex
values anywhere in the library — so a variable change cascades to every component on the page.
The variable system
Variables are declared on :root
for light mode and re-declared on .dark
for dark mode. They come in foreground / background pairs — every surface variable has a
matching -foreground for
readable text on top.
| Variable | Used by |
|---|---|
--color-background / --color-foreground |
Page surface and primary text. |
--color-card / --color-card-foreground |
Card, Sheet, Popover, Dropdown, Dialog body. |
--color-primary / --color-primary-foreground |
Primary button, active links, accent fills. |
--color-secondary / --color-secondary-foreground |
Secondary button, subtle chips. |
--color-muted / --color-muted-foreground |
Muted surfaces (skeletons, code blocks) and secondary text. |
--color-accent / --color-accent-foreground |
Hover backgrounds for menu items, navigation links. |
--color-destructive / --color-destructive-foreground |
Destructive buttons, invalid form rings, error alerts. |
--color-border |
Default border for inputs, cards, dividers. |
--color-input |
Form input borders. |
--color-ring |
Focus-visible ring color. |
--radius |
Base border-radius. Components derive --radius-sm / --radius-xl from it. |
Global override
To re-skin the entire app, override the variables in your own stylesheet loaded
after lumeo.css.
Use OKLCH for colors — the built-in themes do — so contrast stays predictable when you tweak
lightness.
/* my-app.css — loaded after lumeo.css */
:root {
--color-primary: oklch(0.55 0.22 250);
--color-primary-foreground: oklch(0.98 0 0);
--color-ring: oklch(0.55 0.22 250);
--radius: 0.5rem;
}Dark mode swaps
Lumeo does not use Tailwind's dark:
prefix anywhere internally. Dark mode flips the same variables under the
.dark class on
<html>. Your
overrides must declare both blocks or dark mode will fall back to the library defaults for
anything you didn't redeclare.
:root {
--color-background: oklch(1 0 0);
--color-foreground: oklch(0.15 0 0);
--color-primary: oklch(0.55 0.22 250);
--color-primary-foreground: oklch(0.98 0 0);
}
.dark {
--color-background: oklch(0.15 0 0);
--color-foreground: oklch(0.98 0 0);
--color-primary: oklch(0.65 0.18 250);
--color-primary-foreground: oklch(0.15 0 0);
}
The .dark class is
toggled by the ThemeService.
Don't toggle it yourself unless you also persist the choice.
Scoped override
Variables cascade, so you can re-declare them on any ancestor element to re-skin only the subtree below it. Useful for an embedded widget that should look "branded" while sitting inside a neutral app shell.
<div class="embedded-widget" style="--color-primary: oklch(0.6 0.2 30); --color-ring: oklch(0.6 0.2 30);">
<Card>
<Stack Gap="3" Class="p-4">
<Heading Level="3">Promo</Heading>
<Button>Get started</Button>
</Stack>
</Card>
</div>Per-component class overrides
Every Lumeo component accepts Class,
appended to the component's base classes via
$"{BaseClasses} {Class}".Trim().
Use it to add utilities (margins, hover states, sizing). For colors, prefer pointing at theme
variables — bg-primary,
text-foreground — so the
override still respects light and dark mode.
<!-- good: theme-aware utilities --> <Button Class="bg-success text-success-foreground hover:bg-success/90">Mark complete</Button> <!-- good: spacing / sizing --> <Card Class="max-w-md mx-auto shadow-xl">...</Card> <!-- avoid: raw hex breaks in dark mode --> <Button Class="bg-[#1e40af] text-white">Don't do this</Button>
Avoid raw hex / rgb in Class.
They look right in light mode and break in dark mode because they don't participate in the
variable swap.
Picking the right layer
| Goal | Layer |
|---|---|
| Brand color across the whole app | Global override of --color-primary + dark variant. |
| Different look for an embedded marketing widget | Scoped override on the widget's wrapper. |
| One specific button needs a custom hue | Per-component Class with theme utilities. |
| Switch users between built-in themes | ThemeService + ThemeSwitcher. |
| Round all corners more | Global override of --radius. |
Demo: scoped theme
Wrap any subtree in a div that re-declares the variables. The card and button below pick up a violet accent without affecting anything else on this page.
Scoped accent
This card lives inside an override block.
See also
- Theme Service — toggling and persisting themes
- Theme Switcher — built-in switcher component
- Theme Toggle — light/dark toggle
- Accessibility — contrast guarantees