CSS relative colour and OKLCH for WordPress themes

Theming a WordPress site has meant one of two things for years: piles of CSS overrides, or a Sass map so tangled that a client asking for a slightly different brand blue breaks it. I thought I had seen every way a design system can rot into structural debt. Then CSS Relative Colour showed up and made the systemic version of this workable.

Hand-calculating HEX values for hover states and dark mode is slow work, and it leaves you with CSS that snaps the first time a base color moves. Modern CSS lets you describe color as a system instead of a list of fixed values. CSS Relative Colour syntax paired with the OKLCH color space is what makes that hold up over the life of a project.

Why manual palettes break

A typical WordPress project defines custom properties for the brand colors and stops there. Then you need a darker blue for a border or a lighter one for a background, so you open Sketch or Figma, pull a new HEX value and paste it in. Nothing connects after that: change --brand-primary and every color derived from it has to be updated by hand.

I have refactored enough legacy themes to read manual calculation as a promise of bugs later. What you want is a way to say: take this color, move its lightness by 10%, hand me the result.

Why OKLCH suits relative adjustments

OKLCH is perceptually linear. In HSL, 50% lightness reads brighter or duller depending on the hue you picked. In OKLCH, 50% lightness looks like 50% lightness whatever the hue is, which is what you need before doing arithmetic on it. The relative syntax then lets you pull a color apart channel by channel 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);
}

The from keyword tells the browser to convert the origin color into OKLCH channels, even when you wrote it as a HEX: L for lightness, C for chroma, H for hue. Each channel is then a number you can do math on. My guide on clean SVG CSS custom properties pushes the same idea further.

Animating the channels

The trouble starts when you try to animate one of these transitions. Interpolate between two colors the ordinary way and the browser often routes through a muddy grey middle. Registering typed custom properties with @property avoids that, because you can then animate a single channel of the CSS Relative Colour system instead of the whole color.

/* 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 the values tells the browser they are numbers rather than strings, so it can interpolate them frame by frame. I have used exactly this to build breathing UI elements whose glow shifts with what the user is doing, with no JavaScript involved. There is more on avoiding heavy JS in my post on native scroll-based animations.

If wiring this into a theme is eating your dev hours, it is the kind of work I take on. I have been building in WordPress since the 4.x days.

Working from one foundation color

The move is to stop keeping color as a set of disconnected values and instead define one foundation, then describe everything else as a relationship to it. Theming gets easier and the project stays consistent as it grows. When the client asks to swap brand blue for brand teal, you edit one variable and the hover states, gradients and animations follow.

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.