Theming

Eluan uses a three-layer token architecture controlled through HTML data attributes. Themes, modes, spacing scale, and border radius are all swappable at runtime — set them imperatively or with the EluanProvider. One theme ships built in; your brand's look is a custom theme you define.

Overview

Eluan's theming system is built on CSS custom properties with three distinct layers:

  1. Primitives — 20 color palettes (19 with shades 50–950, plus mono with an extra 0 for pure white), named like --color-blazeorange-500. Plus radius, sizing, spacing, viewport, and font-size scales.
  2. Modes — Light, dim, and dark mode mappings. Activated via data-mode.
  3. Themes — Accent color and font pairings layered on top of mode tokens. Activated via data-theme. Eluan ships one built-in theme, minimal; you add your own with createTheme.

Data Attributes

All theming is controlled through five HTML data attributes. The EluanProvider writes these for you, but you can also set them by hand. Four are always present; data-typeset is the exception — its default is to be absent, which is what keeps type fluid.

Themes

One theme ships out of the box. It defines an accent color, font pairing, and surface treatment — and it is deliberately neutral, because themeability is the point: your brand's look is a theme you define, not one Eluan picked for you. Use createTheme to build your own from a handful of token overrides, or generate a complete set with @eluan/theme-generator.

minimal

The only built-in theme, and the default. A neutral typographic baseline: restrained, near-monochrome surfaces with a single accent. Every other look is a custom theme you define.

Modes

Eluan supports three light levels: light, dim, and dark. Each maps the same semantic token names to different primitive values, so components don't need to change at all when you switch.

Spacing Scale

data-spacing adjusts the base spacing unit:

  • compact — Denser layouts, tighter padding.
  • standard — Default spacing (recommended).
  • wide — More breathing room, larger touch targets.

Border Radius

data-curves controls the global border radius scale:

  • sharp — Fully squared off.
  • slight — Subtle rounding. Default.
  • sweeping — Friendly, large rounding.

Typeset

data-typesetcontrols the type scale — and it behaves differently from the other axes. Eluan's ten typeset steps (--font-size-step-6 down to --font-size-step-neg3) are viewport-fluid by default, interpolating between three anchors at 480px, 748px, and 1024px. So the useful default is no attribute at all.

  • auto — the default. No attribute is written; every step follows the viewport.
  • small / medium / large— pins every step to that column's static size.

The attribute is not limited to <html>. Put it on any element to pin a subtree — a compact sidebar, a print view — while the rest of the page stays fluid. See the Design Tokens page for the step table, and Typographyfor the React component that applies a step's size, line height, and tracking together.

EluanProvider

EluanProvider is the recommended way to manage these attributes. It writes them to <html>on every change, lazy-loads the theme's fonts, persists choices to localStorage, and optionally follows the OS prefers-color-scheme.

With persist, choices are stored under eluan:theme, eluan:mode, eluan:spacing, eluan:curves, and eluan:typeset. Typeset is the odd one out: only an explicit pin is written. "auto"means “no preference”, so it clears the key instead of storing a value.

Avoiding a flash on SSR

Server-rendered markup ships before the provider mounts, so a user with stored preferences briefly sees the defaults. Inject a small inline script in <head>, before the app bundle, to set the attributes up front. Note the auto guard on typeset — writing data-typeset="auto" would pin the scale to a non-existent column instead of leaving it fluid.

In Next.js, render this with next/script at strategy="beforeInteractive".

Custom Themes

Use createTheme to override only the tokens you care about — the rest (roughly 150 of them) fall through from the base theme you extend. extends defaults to minimal, the only built-in, so it rarely needs to be set. Custom themes are registered with the provider and selected by name exactly like the built-in.

Starting from brand colors rather than individual tokens? @eluan/theme-generator turns one to three accent colors into a full, contrast-checked light + dark token set you can drop straight into createTheme. Build one in the browser on the Theme Generator page.