Every frontend project starts the same way.

:root { --primary: #f59e0b; --primary-hover: #d97706; } 

At first, it looks harmless.

Then the requirements grow.

:root { --primary: #f59e0b; --primary-hover: #d97706; --primary-active: #b45309; --primary-border: #ea9a00; --primary-shadow: rgba(245, 158, 11, 0.2); } 

Somehow, one color became five.

And every time the design team changes the brand color, we repeat the same process:

  • Open the color picker
  • Make it darker for hover
  • Make it even darker for active
  • Adjust the shadow color
  • Hope everything still looks consistent

I never questioned this workflow.

Until I discovered OKLCH.

The Problem We Pretend Doesn't Exist

Imagine our primary color is:

--primary: #ffa000; 

Now we need a hover color.

What do we do?

Usually something like:

--primary-hover: #f29000; 

But how did we get that value?

Most of the time:

  • Trial and error
  • Design tool adjustments
  • Visual guessing

There is no real system.

We're manually creating color variations instead of describing how a color should change.

Then I Found This Weird Syntax

One day I came across this:

background: oklch( from var(--primary) calc(l - 0.05) c h ); 

My first reaction:

Wait...

I can generate the hover color from the original color?

No extra color variable?

  • Yes

What OKLCH Changed for Me

Instead of storing multiple versions of the same color:

--primary --primary-hover --primary-active 

I only keep the source color:

--primary: oklch(0.75 0.18 70); 

Then create variations on demand.

Hover:

background: oklch( from var(--primary) calc(l - 0.05) c h ); 

Active:

background: oklch( from var(--primary) calc(l - 0.1) c h ); 

Focus:

background: oklch( from var(--primary) calc(l + 0.05) c h ); 

The color stays the same.

Only its lightness changes.

The Moment It Clicked

Traditional CSS asks:

What should the hover color be?

OKLCH asks:

How should the base color change?

That small shift in thinking makes a huge difference.

Before:

:root { --success: #22c55e; --success-hover: #16a34a; --warning: #f59e0b; --warning-hover: #d97706; --error: #ef4444; --error-hover: #dc2626; } 

After:

:root { --success: oklch(...); --warning: oklch(...); --error: oklch(...); } 

And every hover color is generated automatically:

button:hover { background: oklch( from var(--button-color) calc(l - 0.05) c h ); } 

No duplicate color tokens & No maintenance burden.

Tailwind Is Already Doing This

Many developers still think in HEX and RGB.

The industry isn't moving toward OKLCH because the syntax is cool.

It's moving toward OKLCH because generating color variations mathematically is more reliable than manually picking dozens of related colors.

The Hidden Benefit During Rebranding

Imagine the design team changes the primary color.

Before:
All need review.

After:
Change one value.

Everything derived from it updates automatically.

Hover, Active, Focus, Shadows, Borders, and Glows.

All stay consistent.