Color Model: OKLCH

Espalier uses OKLCH as its internal color model. OKLCH describes colors using three axes: L (perceived lightness, 0–1), C (chroma / color intensity, 0–0.37+), and H (hue angle, 0–360).

This model drives the entire palette generation pipeline: seed color input, color-theory variant generation, semantic token derivation, and APCA contrast adjustment use OKLCH. Contrast scoring converts the colors to sRGB for APCA before the final values are emitted as CSS custom properties.

What this means when writing theme colors

The seedColor field accepts opaque OKLCH, hexadecimal, RGB, and HSL colors. Other CSS color spaces and named keywords are not parsed. See the guide overview. For example:

// Any of these are valid seed colors
{ seedColor: 'oklch(0.65 0.18 240)' }  // OKLCH — direct
{ seedColor: '#3b82f6' }               // hex — converted internally
{ seedColor: 'hsl(217 91% 60%)' }      // HSL — converted internally

Using OKLCH directly gives you the most predictable results because the numbers you write correspond to perceived brightness (L) and color intensity (C) rather than hardware channel values.

OKLCH in application CSS

Because --esp-color-* tokens are emitted as OKLCH values, you can use Relative Color Syntax to derive related values in your own CSS:

/* Lighten a token by shifting its L channel */
background: oklch(from var(--esp-color-primary) calc(l + 0.05) c h);

/* Desaturate by reducing chroma */
color: oklch(from var(--esp-color-text) l calc(c * 0.5) h);

Lightness ramp tokens

Espalier also exposes the raw lightness values it uses internally as --esp-l-* tokens (e.g. --esp-l-surface, --esp-l-text). These are useful when you need to apply the same perceptual lightness levels to custom colors that are outside the standard palette.

Components API Guides Getting started Styling Espalier Browser support GitHub npm package Taproot I/O