Theming

Customize elorm/ui design tokens, color presets, and dark mode.

CSS Variables#

elorm/ui uses OKLCH color tokens defined in your global CSS file. All components reference semantic tokens like bg-primary, text-muted-foreground, and border-border.

During elorm init, you choose a base color, accent, and radius. These are written to elorm.json and applied to your CSS scaffold automatically.

<div className="bg-background text-foreground" />
<div className="bg-primary text-primary-foreground" />

Token convention#

We use semantic background and foreground pairs. The base token controls the surface color; the -foreground token controls text and icons on that surface.

Given:

--primary: oklch(0.45 0.18 252);
--primary-foreground: oklch(0.985 0 0);

This component uses var(--primary) for background and var(--primary-foreground) for text:

<div className="bg-primary text-primary-foreground">Hello</div>

Theme tokens#

TokenWhat it controlsUsed by
background / foregroundDefault app background and textPage shell, sections
card / card-foregroundElevated surfacesCard, dashboard panels
popover / popover-foregroundFloating surfacesDialog, dropdown, select
primary / primary-foregroundHigh-emphasis actionsButton default, active states
secondary / secondary-foregroundLower-emphasis actionsSecondary buttons, badges
muted / muted-foregroundSubtle surfaces and textDescriptions, placeholders
accent / accent-foregroundHover and focus surfacesGhost buttons, menu highlights
destructiveDestructive actionsDestructive buttons, errors
borderDefault bordersCards, menus, separators
inputForm control bordersInput, textarea, select
ringFocus ringsAll focusable controls
chart-1chart-5Chart paletteDashboard charts
radiusBase corner radiusCards, inputs, buttons

Radius scale#

--radius is the base radius token. A scale is derived from it:

@theme inline {
  --radius-sm: calc(var(--radius) * 0.6);
  --radius-md: calc(var(--radius) * 0.8);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) * 1.4);
  --radius-2xl: calc(var(--radius) * 1.8);
}

Init radius presets:

PresetValue
default0.625rem
compact0.375rem
round0.75rem

Base colors#

Available base colors: neutral, zinc, slate, stone, gray.

Apply a preset after init:

Accent colors#

Available accents: default, mono, blue, violet, green, orange, rose, amber, cyan.

Mono keeps buttons and focus rings achromatic — pair it with the neutral or zinc base to compare both palettes side by side on the site theme picker.

Accents update --primary, --ring, and chart tokens:

Dark mode#

Add the dark class to your <html> element to enable dark mode tokens. Components automatically use the correct semantic colors.

<html className="dark">

Customizing tokens#

Edit CSS variables in your global stylesheet. Components pick up changes without modifying individual files.

To add a custom token, define it under :root and .dark, then expose it via @theme inline:

:root {
  --warning: oklch(0.84 0.16 84);
  --warning-foreground: oklch(0.28 0.07 46);
}
 
.dark {
  --warning: oklch(0.41 0.11 46);
  --warning-foreground: oklch(0.99 0.02 95);
}
 
@theme inline {
  --color-warning: var(--warning);
  --color-warning-foreground: var(--warning-foreground);
}
<div className="bg-warning text-warning-foreground" />

Always use semantic tokens in your own code — avoid raw color utilities like bg-blue-500.

Default theme scaffold#

The following is the neutral default theme. Copy and adjust as needed:

@import "tailwindcss";
 
@custom-variant dark (&:is(.dark *));
 
@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);
  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);
  --color-destructive: var(--destructive);
  --color-border: var(--border);
  --color-input: var(--input);
  --color-ring: var(--ring);
  --radius-sm: calc(var(--radius) * 0.6);
  --radius-md: calc(var(--radius) * 0.8);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) * 1.4);
}
 
:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  /* ... remaining tokens */
}
 
.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  /* ... remaining tokens */
}

Run elorm init to generate a complete scaffold tailored to your chosen base color and accent.

Runtime theme switching#

For light / dark / system mode toggling in your app, see Dark Mode.