Building Maintainable Design Systems with CSS Relative Colour

We need to talk about how we theme WordPress sites. For years, the industry standard has been massive blocks of CSS overrides or complex Sass maps that break the second a client asks for a slightly different brand blue. I honestly thought I’d seen every way a design system could collapse into a pile of structural debt, then CSS Relative Colour entered the chat and finally made systemic theming viable.

If you’re still manually calculating HEX codes for hover states or dark mode, you’re essentially sprinting a marathon with a lead vest. Consequently, your code becomes brittle. Modern CSS now allows us to treat color as a dynamic system rather than a static value. Specifically, the combination of CSS Relative Colour syntax and the OKLCH color space is a game-changer for maintainability.

The Fragility of Manual Palettes

In a typical WordPress project, we define a set of custom properties for brand colors. However, when we need a “darker blue” for a border or a “lighter blue” for a background, we usually go back to Sketch or Figma, grab a new HEX value, and hardcode it. This creates a disconnect. If the base --brand-primary changes, you have to manually update every derived child color.

This is where things get messy. I’ve refactored enough legacy themes to know that “manual calculation” is just another term for “eventual bugs.” We need a way to say: “Take this color, tweak its lightness by 10%, and give me the result.”

Why OKLCH and CSS Relative Colour Win

The OKLCH color space is perceptually linear. Unlike HSL, where 50% lightness looks different depending on the hue, 50% lightness in OKLCH always looks like 50% lightness to the human eye. Therefore, it’s the perfect target for relative adjustments. Furthermore, with the new relative syntax, we can “destructure” a color directly in CSS.

/* The Naive, Brittle Approach */
:root {
  --brand-blue: #5accd6;
  --brand-blue-dark: #3f9097; /* Manually calculated... annoying */
}

/* The Architect's Approach using CSS Relative Colour */
:root {
  --brand-blue: #5accd6;
  --brand-blue-dark: oklch(from var(--brand-blue) calc(l - 0.20) c h);
  --brand-blue-light: oklch(from var(--brand-blue) calc(l + 0.10) c h);
}

By using the from keyword, the browser converts the origin color (even if it’s a HEX) into OKLCH channels: L (lightness), C (chroma), and H (hue). Consequently, you can perform math on these channels individually. If you want to master more advanced implementations, checking out my guide on clean SVG CSS custom properties is a great next step.

Beyond Static Colors: Animating the System

The real “war story” starts when you try to animate these transitions. Traditionally, if you animate between two colors, the browser interpolates the values in a way that often passes through “muddy” or greyish middle-grounds. However, by registering typed custom properties using @property, we can animate the individual channels of the CSS Relative Colour system.

/* Registering channels for smooth interpolation */
@property --f-l {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.78;
}

@property --f-c {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.10;
}

@property --f-h {
  syntax: "<number>";
  inherits: true;
  initial-value: 200;
}

.dynamic-element {
  --foundation: oklch(var(--f-l) var(--f-c) var(--f-h));
  background: var(--foundation);
  transition: --f-l 0.5s ease;
}

.dynamic-element:hover {
  --f-l: 0.90; /* Only animate the Lightness channel */
}

Registering these values tells the browser to treat them as numbers, not strings. This allows for frame-by-frame interpolation that looks incredibly natural. I’ve used this exact technique to build “breathing” UI elements that shift their glow based on user interaction without a single line of JavaScript bloat. For more on ditching heavy JS, read about native scroll-based animations.

Look, if this CSS Relative Colour stuff is eating up your dev hours, let me handle it. I’ve been wrestling with WordPress since the 4.x days.

The Shift to Systemic Design

In summary, stop treating color as a collection of disjointed values. Instead, define a foundation and describe the relationships. This approach doesn’t just make theming easier; it makes your entire project more consistent and future-proof. When the client inevitably asks to change the “brand blue” to a “brand teal,” you change one variable, and the entire system—hover states, gradients, and animations—scales perfectly.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.

Leave a Comment

Your email address will not be published. Required fields are marked *