by ASW Team 8 min read

Understanding CSS Custom Properties

A deep dive into native CSS design tokens, the oklch colour space, and building a token system without preprocessors.

Why Custom Properties?

CSS custom properties (often called CSS variables) let you store values and reuse them throughout your stylesheet. Unlike preprocessor variables ($ in Sass or Less), custom properties are live — they cascade, can be overridden at any level, and can be changed at runtime.

"Custom properties are to design tokens what functions are to programming — they give you a single source of truth that ripples through the entire system."

When you define a value once as a custom property, every reference to it updates automatically when the property changes. This is the foundation of a maintainable design system.

The oklch Colour Space

Traditional rgb and hsl colour spaces are device-dependent and perceptually uneven. The oklch colour space, introduced in CSS Color Level 4, is designed for human perception — a 10% lightness shift looks like a 10% lightness shift, regardless of the hue.

/* Traditional RGB — hard to reason about */
--accent: #4a6cf7;

/* oklch — perceptually uniform, easy to tweak */
--accent: oklch(65% 0.15 250);

The three components of oklch are:

Why This Matters for Design Systems

Because oklch uses a polar coordinate system (like HSL), you can rotate hues while keeping perceptual lightness constant. This makes generating colour scales, accent colours, and dark mode variants straightforward.

:root {
  --palette-hue: 250;        /* blue-violet */
  --palette-chroma: 0.15;

  --accent: oklch(65% var(--palette-chroma) var(--palette-hue));
  --accent-hover: oklch(72% var(--palette-chroma) var(--palette-hue));
}

Building a Token System

A design token system organises custom properties into semantic layers:

  1. Primitives — raw values (sizes, colours, fonts)
  2. Aliases — semantic names mapped to primitives
  3. Component tokens — role-specific values

For example, a spacing scale starts with raw sizes:

--size-1: 0.25rem;   /*  4px */
--size-2: 0.5rem;     /*  8px */
--size-3: 1rem;       /* 16px */

Then semantic aliases map those sizes to their roles:

--space-1: var(--size-1);  /* tiny gap */
--space-2: var(--size-2);  /* tight gap */
--space-4: var(--size-3);  /* standard gap */

Live Reload in the Browser

One of the superpowers of custom properties is live editing. Open your browser's dev tools, change a token value in :root, and watch every element that uses it update in real time.

/* Change this in dev tools and see the whole page shift */
:root {
  --palette-hue: 200;  /* shift from blue-violet to blue */
}

Common Pitfalls

Custom properties are powerful, but they come with some gotchas:

Conclusion

CSS custom properties, combined with the oklch colour space, give you everything you need for a modern design token system — no preprocessor required. It's more verbose in places, but the trade-off is runtime flexibility, browser-native live editing, and zero build dependencies.

Footnotes

  1. The oklch specification is part of CSS Color Module Level 4. Read the spec.
  2. Browser support for oklch is excellent — all modern browsers support it as of 2024. Check CanIUse.