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:
+-
+
- L — Lightness (0% to 100%) +
- C — Chroma or saturation (0 to ~0.4) +
- H — Hue (0 to 360 degrees) +
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:
+ +-
+
- Primitives — raw values (sizes, colours, fonts) +
- Aliases — semantic names mapped to primitives +
- 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:
+ +-
+
- Fallback values — always provide a fallback:
var(--custom, fallback)
+ - Inheritance — custom properties inherit by default. Use
@propertyfor controlled behaviour.
+ - Animation — not all custom property types can be animated without
@property
+ - Performance — thousands of unique custom properties in a single scope can slow repaints +
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
+-
+
- The
oklchspecification is part of CSS Color Module Level 4. Read the spec.
+ - Browser support for oklch is excellent — all modern browsers support it as of 2024. Check CanIUse. +
+ + + + +