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

Style presets#

The default style is elorm. The beta style is nagomi, a calmer preset with larger default radii, softer borders, and smoother elevated surfaces.

{
  "style": "nagomi"
}

Start a Nagomi project with:

Nagomi keeps the same base color and accent system, but tunes the generated CSS tokens around rounded cards, softer form borders, smoother overlays, and calmer feedback components. Selected registry output also receives Nagomi source styling for buttons, inputs, textareas, select, dialog, sheet, popover, dropdown menu, badge, alert, empty state, stat card, progress, and slider.

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.