OKLCH Color Space Explained: Why Your Gradients Look Muddy
Why OKLCH interpolation makes smoother CSS gradients than RGB or HSL, with worked examples and a link to the FontBlend gradient generator.
If you have ever built a CSS gradient between two colors and noticed a muddy brown band in the middle, the problem is your color space. RGB and HSL interpolate colors in a way that passes through desaturated middle ground. OKLCH interpolates in a perceptually uniform space, which keeps the gradient vivid from end to end. This article explains why.
What OKLCH Is
OKLCH is a color space with three channels: Lightness, Chroma, and Hue. It was introduced as part of the CSS Color Module Level 4 specification. The L channel is perceptual lightness (how light or dark a color looks to a human eye), C is chroma (how colorful it is, from gray to fully saturated), and H is hue (the color wheel position, 0 to 360 degrees).
The key property of OKLCH is perceptual uniformity. A change of 0.1 in Lightness looks like the same amount of change whether you are moving from black to dark gray or from white to light gray. RGB and HSL do not have this property: a change of 10% in the blue channel looks different depending on what the red and green channels are doing.
Why RGB Gradients Look Muddy
When you ask CSS to interpolate a gradient from blue to yellow in RGB, it walks the straight line through the RGB cube. The midpoint of that line is a desaturated grayish brown, because the straight-line path from blue (low R, low G, high B) to yellow (high R, high G, low B) passes through the center of the cube, where all three channels are equal.
The gradient looks muddy because the perceptual midpoint of blue and yellow is not gray. It is a vivid green or teal. But RGB does not know about perception. It only knows about channel values.
Why HSL Is Not Much Better
HSL was designed to be more intuitive than RGB. It separates hue, saturation, and lightness, which sounds like it should solve the gradient problem. But HSL's lightness is not perceptually uniform. A 50% lightness in HSL is not the same perceived brightness for all hues. Yellow at 50% HSL lightness is much brighter than blue at 50% HSL lightness.
So an HSL gradient from blue to yellow still has a perceptual bump. The midpoint is more saturated than the RGB midpoint, but the lightness does not move smoothly. The gradient looks uneven.
How OKLCH Fixes It
OKLCH interpolates in a space where lightness is perceptual. When you ask CSS to interpolate from blue to yellow in OKLCH, it walks the path that keeps perceptual lightness constant and moves hue and chroma smoothly. The midpoint is a vivid green or teal that is the perceptual average of blue and yellow.
The result is a gradient that looks even from end to end, with no muddy band and no lightness bump.
Browser Support
OKLCH is supported in all major browsers since 2023. You can use it today in production. The CSS syntax is oklch(0.7 0.15 250) for a lightness of 0.7, chroma of 0.15, and hue of 250 degrees.
For gradients, you can write linear-gradient(to right, oklch(0.5 0.2 250), oklch(0.7 0.15 60)) and the browser interpolates in OKLCH. Older browsers that do not support OKLCH will ignore the declaration and fall back to whatever you specify before it.
When OKLCH Matters
OKLCH matters most for gradients between colors that are far apart on the color wheel (blue to yellow, red to green, purple to yellow). These are the gradients that look muddy in RGB.
It matters less for gradients between similar hues (light blue to dark blue, light red to dark red). These look fine in RGB because the path stays in a saturated region of the color space.
It also matters for color palette generation. If you generate a palette by adjusting lightness in HSL, the lighter and darker shades will look uneven because HSL lightness is not perceptual. If you adjust lightness in OKLCH, the shades look even.
The FontBlend Gradient Generator
The FontBlend gradient generator offers OKLCH as a selectable interpolation space, alongside sRGB, OKLab, and HSL. You pick two or more colors, switch the interpolation to OKLCH, and the generator emits the CSS in oklch syntax that tells the browser to walk the OKLCH path between your stops. You can compare the OKLCH output to the sRGB output side by side to see the difference.
The color palette generator and the brand kit builder also use OKLCH for their contrast-aware palette suggestions. When the brand kit suggests a lighter or darker variant of your primary color, it adjusts OKLCH lightness, which keeps the perceived saturation constant.
Reading an Oklch() Value
Take the value oklch(0.7 0.15 250). The first number is lightness on a zero to one scale, where 0 is black and 1 is white, and 0.5 looks like a middle gray to the eye rather than a technical midpoint. The second is chroma, the distance from a neutral gray of the same lightness; values above about 0.3 are strongly saturated in practice, and the usable ceiling depends on the hue and the lightness. The third is hue in degrees around the wheel, with 250 landing in the blue region.
The channel order is deliberate. Because lightness is perceptual, you can change the first number and leave the other two alone and get a shade that really looks lighter or darker, not just a value that is numerically different. That is the property designers exploit when they build a button ramp from one base color: five rows with lightness 0.3 to 0.7 look evenly spaced, where the same exercise in HSL or hex produces a ramp with a dark, cramped bottom and a pale, washed-out top.
The Same Gradient, Three Interpolation Modes
A gradient that spans a wide arc of the wheel exposes the differences fastest. Blue to yellow in sRGB interpolates channel by channel, and the three channels cross near the point where red, green, and blue are all equal, which is why the middle of that gradient reads gray. Interpolate in HSL and the middle is more colorful but the perceived brightness bulges, because HSL lightness does not match how the eye weighs different hues. Interpolate in OKLCH and the path holds lightness steady while hue travels, so the midpoint lands on the teal the eye actually expects between blue and yellow.
The same logic applies to any pair of distant stops. Colors close to each other on the wheel, like two blues or a red and an orange, barely change the answer, because the straight sRGB line between them stays inside a saturated region. When in doubt, look at the distance between the two hues: far apart means the interpolation space matters, close together means it does not.
Using OKLCH Beyond Gradients
The practical wins extend past gradients. Generating a full scale from a brand color is the second big use: step the lightness channel in OKLCH and every step keeps the same hue and roughly the same vividness, which is exactly what a UI color ramp wants. The third use is picking a darker text color that still reads as the same brand hue, the kind of decision the contrast checker automates by shifting the foreground until it clears the WCAG bar.
The syntax also helps teams that keep colors in code review. A diff that says lightness changed from 0.65 to 0.55 tells a reviewer what actually happened to the color, where a hex diff of #a7c2ea to #8ba9d4 hides the change in three channels at once.
Questions That Come Up
Is OKLCH the same as OKLab? OKLab is the three-channel perceptual space, and OKLCH is the cylindrical form of it, lightness, chroma, and hue, in the same way HSL is the cylindrical form of RGB. CSS lets you write both, and gradients can interpolate in either.
Do I need a fallback for old browsers? Yes, if your audience includes browsers from before 2023. Write a hex or rgb declaration first and the oklch one second; browsers that cannot parse the newer syntax keep the earlier value.
Can I use OKLCH for solid colors in production? Yes, as a color value anywhere a color is accepted, and tools such as the color palette generator display OKLCH alongside the hex for exactly that reason. The one place to stay in sRGB is WCAG contrast math, which is defined against sRGB.
How do I convert existing hex colors to OKLCH? A modern browser console or the palette generator will show the oklch equivalent of any hex value, and the round trip is close enough for design work. Migrate a palette in small steps, converting the values you touch rather than rewriting every color at once.
Related Tools
The gradient generator produces OKLCH-interpolated gradients. The color palette generator includes OKLCH values for every swatch. The contrast checker computes WCAG ratios on the actual sRGB values, which is correct because WCAG is defined in sRGB.