Understanding OKLCH: The Future of CSS Color
Embed This Widget
Add the script tag and a data attribute to embed this widget.
Embed via iframe for maximum compatibility.
<iframe src="https://colorfyi.com/iframe/entity//" width="420" height="400" frameborder="0" style="border:0;border-radius:10px;max-width:100%" loading="lazy"></iframe>
Paste this URL in WordPress, Medium, or any oEmbed-compatible platform.
https://colorfyi.com/entity//
Add a dynamic SVG badge to your README or docs.
[](https://colorfyi.com/entity//)
Use the native HTML custom element.
For most of the web's history, designers and developers worked with the same three color formats: HEX, RGB, and HSL. These formats are deeply familiar, but they share a common flaw — they do not map closely to how human vision actually perceives color. OKLCH is a newer color format that solves this problem. It is now supported in all major browsers, and it is quickly becoming the preferred way to define color in modern CSS.
Why OKLCH Matters
Before OKLCH, the dominant "human-friendly" format for CSS was HSL — Hue, Saturation, Lightness. HSL was an improvement over HEX and RGB because it let you think in terms of the color wheel. You could say "rotate the hue by 30 degrees" or "reduce the saturation" and get predictable results.
But HSL has a serious hidden flaw: its lightness value is not perceptually uniform. Two colors at the same HSL lightness can look dramatically different in brightness to the human eye. For example:
hsl(60, 100%, 50%)— a vivid yellowhsl(240, 100%, 50%)— a vivid blue
Both have 50% lightness in HSL, but yellow appears much brighter than blue to human observers. This is because our eyes have different sensitivity to different wavelengths of light. The cone cells in our retina are most sensitive to yellow-green light and least sensitive to blue.
This inconsistency causes real problems in design systems. If you generate a color scale by incrementing HSL lightness, the resulting shades will feel inconsistent — some steps will look like big jumps, others like barely any change at all.
OKLCH fixes this by being perceptually uniform: equal numerical changes in lightness produce equal-looking changes in perceived brightness, regardless of hue.
What OKLCH Stands For
OKLCH is a cylindrical representation of the Oklab color space. The name breaks down as:
- OK — the "Ok" prefix refers to the Oklab color space, designed by Björn Ottosson in 2020
- L — Lightness (perceptual), from 0 (black) to 1 (white)
- C — Chroma, the colorfulness or vividness of the color (similar to saturation)
- H — Hue, the angle on the color wheel in degrees (0–360)
The format in CSS looks like this:
color: oklch(0.7 0.15 240);
That example means: 70% lightness, chroma of 0.15, and a hue of 240 degrees (blue range).
You can use the Color Converter to convert any HEX or RGB color to its OKLCH equivalent and back.
Perceptual Uniformity Explained
The concept of perceptual uniformity is central to understanding why OKLCH is superior. Let us compare what happens when you change the lightness of a blue versus a yellow in both HSL and OKLCH.
In HSL (not perceptually uniform)
| Color | HSL | Perceived brightness |
|---|---|---|
| Dark yellow | hsl(60, 80%, 30%) |
Moderate |
| Dark blue | hsl(240, 80%, 30%) |
Much darker |
| Light yellow | hsl(60, 80%, 70%) |
Very bright |
| Light blue | hsl(240, 80%, 70%) |
Noticeably duller |
The same lightness values produce dramatically different perceived brightness depending on hue.
In OKLCH (perceptually uniform)
| Color | OKLCH | Perceived brightness |
|---|---|---|
| Dark yellow | oklch(0.55 0.14 90) |
Moderate |
| Dark blue | oklch(0.55 0.14 270) |
Moderate |
| Light yellow | oklch(0.82 0.14 90) |
Bright |
| Light blue | oklch(0.82 0.14 270) |
Equally bright |
The same L value produces consistent perceived brightness regardless of the hue. This is the perceptual uniformity that makes OKLCH so powerful for systematic design work.
OKLCH vs HSL: A Practical Comparison
Hue Interpolation in Gradients
One place where OKLCH shows a clear advantage over HSL is color interpolation — how CSS calculates the intermediate colors in a gradient.
In HSL, a gradient from red to blue often passes through an ugly grey or muddy intermediate because RGB interpolation at low saturation produces desaturated midpoints. In OKLCH, gradients interpolate through vivid, well-saturated midpoints because the color space is designed around human perception.
/* HSL gradient — may go grey in the middle */
background: linear-gradient(in hsl, red, blue);
/* OKLCH gradient — stays vivid throughout */
background: linear-gradient(in oklch, red, blue);
Consistent Shade Generation
The most practical benefit of OKLCH for design systems is consistent shade generation. If you start with a brand color and want to create a 10-step scale from light to dark, OKLCH gives you predictable, visually even steps.
For example, starting with a teal #0D9488 and building out a scale using fixed OKLCH L increments, each shade will look like an equal visual "step" from the previous one. In HSL, you would need to manually adjust the increments depending on the hue.
Accessing Wide Gamut Colors
OKLCH has another major advantage: it can describe colors outside of the sRGB gamut. Modern displays like Apple's Liquid Retina XDR screens support the Display P3 color space, which is roughly 35% larger than sRGB. Colors in P3 appear more vivid — deeper reds, more brilliant greens, richer blues.
HEX, RGB, and HSL are all limited to sRGB. OKLCH can address the full P3 gamut and beyond. Here is a vivid green that only P3 displays can fully render:
/* This saturated green is outside sRGB */
color: oklch(0.8 0.3 145);
/* Equivalent P3 color using the color() function */
color: color(display-p3 0.15 0.9 0.4);
Browsers on P3-capable displays will render the OKLCH version with the full, expanded vividness. On standard sRGB displays, the browser will gracefully map it to the nearest sRGB equivalent.
Browser Support
OKLCH is supported in all major modern browsers. As of 2025:
- Chrome/Edge: Full support since version 111 (March 2023)
- Firefox: Full support since version 113 (May 2023)
- Safari: Full support since version 15.4 (March 2022) — Safari was actually first
For a production website, you can use OKLCH without fallbacks for the vast majority of visitors. If you need to support very old browsers, you can use @supports with a fallback:
/* Fallback for very old browsers */
color: #0D9488;
/* Modern OKLCH with wide gamut support */
@supports (color: oklch(0 0 0)) {
color: oklch(0.58 0.14 185);
}
You can check the exact OKLCH values for any color using the Color Converter.
Practical Usage in CSS
Basic Syntax
/* oklch(lightness chroma hue) */
color: oklch(0.7 0.15 200);
/* With alpha */
color: oklch(0.7 0.15 200 / 0.8);
/* Using CSS variables */
:root {
--brand-primary: oklch(0.65 0.18 260);
--brand-light: oklch(0.88 0.08 260);
--brand-dark: oklch(0.42 0.18 260);
}
Understanding the L, C, H Values
Lightness (L): - 0 = absolute black - 0.5 = mid tone - 1 = absolute white - Typical usable range: 0.2 to 0.95
Chroma (C): - 0 = neutral grey (no color) - 0.1 = muted, pastel-like - 0.2 = moderate saturation - 0.3+ = very vivid (may exceed sRGB on some hues) - Maximum displayable chroma depends on hue and lightness
Hue (H): - 0 or 360 = red - 60 = yellow - 90 = yellow-green - 120 = green - 180 = cyan - 270 = blue - 300 = violet/purple - 330 = pink/magenta
Building a Color Scale
Here is a practical example of generating a 5-step blue scale with consistent perceived lightness steps:
:root {
--blue-100: oklch(0.95 0.04 260); /* Near white, very light */
--blue-300: oklch(0.78 0.09 260); /* Light blue */
--blue-500: oklch(0.60 0.15 260); /* Mid blue */
--blue-700: oklch(0.42 0.15 260); /* Dark blue */
--blue-900: oklch(0.25 0.10 260); /* Very dark blue */
}
Because OKLCH lightness is perceptually uniform, each step represents an equal-looking jump in perceived brightness. Try achieving that consistency with HSL — it requires manual fine-tuning for each hue.
Using oklch() in color-mix()
The CSS color-mix() function also benefits from OKLCH's perceptual uniformity:
/* Mix two colors 50/50 in OKLCH — result stays vivid */
color: color-mix(in oklch, #FF5733 50%, #3498DB);
Mixing #FF5733 (a vivid orange-red) with #3498DB (a bright blue) in OKLCH gives a vivid intermediate, whereas mixing in RGB or HSL can produce a desaturated, muddy result.
Converting Existing Colors to OKLCH
If you have an existing design system built on HEX or HSL, you do not need to convert everything at once. Here is a practical migration approach:
- Convert key brand colors using the Color Converter
- Use OKLCH for new components and color scales
- Take advantage of
color-mix()in OKLCH for programmatic color manipulation - Use
@supportsfallbacks only if you need to support older browsers
Example Conversions
| HEX | RGB | OKLCH |
|---|---|---|
| #FF5733 | 255, 87, 51 | oklch(0.63 0.24 27) |
| #3498DB | 52, 152, 219 | oklch(0.63 0.14 232) |
| #2ECC71 | 46, 204, 113 | oklch(0.74 0.18 153) |
| #9B59B6 | 155, 89, 182 | oklch(0.54 0.17 300) |
Notice how the two different-hued colors #FF5733 and #3498DB end up with the same L value (0.63), confirming that OKLCH correctly aligns their perceived brightness — something HSL cannot do.
Key Takeaways
- OKLCH is perceptually uniform: equal L increments produce equal-looking changes in brightness across all hues. HSL is not — the same lightness values look different depending on the color.
- OKLCH enables wide gamut: it can address Display P3 and other wide-gamut colors beyond what HEX/RGB/HSL can represent, giving more vivid results on modern displays.
- Browser support is excellent: all major browsers have supported OKLCH since 2022–2023. You can use it in production today.
- Gradients and color mixing look better in OKLCH because interpolation stays vivid and well-saturated.
- Building design system color scales is significantly easier in OKLCH because you get predictably even perceptual steps.
- Use the Color Converter to convert any color to OKLCH and explore how it maps to other formats.
- The syntax is
oklch(lightness chroma hue)with optional alpha:oklch(0.65 0.18 260 / 0.8).