OKLCH Color Space: Why It Is the Future of CSS Color
A practical guide to the OKLCH color space, its advantages over HSL, and how to use it in modern CSS.
The Problem with HSL
For years, HSL (Hue, Saturation, Lightness) has been the go-to color format for developers who want intuitive control over color. Its polar coordinate system makes it easy to reason about: rotate the hue to change the base color, adjust saturation for vibrancy, and dial lightness up or down. It seems ideal for design systems and palette generation.
But HSL has a fundamental flaw: it is not perceptually uniform. This means that colors with the same HSL lightness value do not appear equally bright to the human eye. Consider two colors at the same HSL lightness of 50%:
hsl(60, 100%, 50%)- yellow, which appears very brighthsl(240, 100%, 50%)- blue, which appears quite dark
Both have a lightness of 50%, yet yellow looks dramatically brighter than blue. This happens because HSL's lightness is a mathematical abstraction that does not account for how human vision actually perceives brightness across different hues. The consequence is significant: if you build a design system by setting all your brand colors to the same HSL lightness, they will look inconsistent. Your yellow buttons will appear to glow while your blue buttons look muted.
This perceptual non-uniformity also breaks gradient generation. An HSL gradient from yellow to blue transitions through an awkward dark region in the middle, because the perceptual brightness drops sharply as the hue moves through certain ranges. Palette generation suffers similarly: analogous or monochromatic palettes created by manipulating HSL values produce colors that feel unbalanced.
What Is OKLCH?
OKLCH is a color space designed by Bjorn Ottosson in 2020 as an improvement to the earlier CIELAB (Lab) and CIELCh color spaces. The "OK" in OKLCH stands for its design goal: to be a simple, practical color space that gets perceptual uniformity "okay" (i.e., good enough for real-world use) without the mathematical complexity of more precise models.
OKLCH uses three channels in a polar coordinate system:
- L (Lightness): Ranges from 0 (black) to 1 (white). Unlike HSL, this value is perceptually uniform: a lightness of 0.5 in OKLCH looks like medium brightness regardless of the hue.
- C (Chroma): Represents color intensity or vibrancy. Ranges from 0 (gray/achromatic) to approximately 0.4 for the most saturated colors in the sRGB gamut. Higher values mean more vivid colors.
- H (Hue): The angle on the color wheel, from 0 to 360 degrees. Similar to HSL hue, but with more perceptually uniform spacing between hues.
The key innovation is in the lightness dimension. When you set two OKLCH colors to the same L value, they genuinely appear to have the same brightness. This makes OKLCH a reliable foundation for generating consistent palettes, creating smooth gradients, and defining accessible contrast relationships.
The oklch() CSS Function
CSS Color Level 4 introduced the oklch() function, which allows you to specify colors directly in the OKLCH color space. The syntax is:
/* oklch(lightness chroma hue) */
color: oklch(0.7 0.15 150);
/* With alpha/opacity */
color: oklch(0.7 0.15 150 / 0.5);
/* Using percentages for lightness */
color: oklch(70% 0.15 150);
/* Using "none" for achromatic colors */
color: oklch(0.5 0 none); /* medium gray */The values are space-separated (following the modern CSS color syntax convention), and the optional alpha is appended with a forward slash. Lightness can be specified as a number (0 to 1) or a percentage (0% to 100%). Chroma is always a number. Hue is specified in degrees (the deg unit is implied).
Browser Support
As of early 2025, oklch() is supported in all major browsers: Chrome 111+, Firefox 113+, Safari 15.4+, and Edge 111+. This covers over 95% of global browser usage. For the small percentage of older browsers, you can provide a fallback:
/* Fallback strategy */
.element {
color: #4f46e5; /* hex fallback for old browsers */
color: oklch(0.53 0.21 265); /* oklch for modern browsers */
}CSS processes these declarations top-to-bottom. Browsers that understand oklch() will use the second declaration, while older browsers will ignore it and use the hex fallback. This progressive enhancement approach is zero-risk.
Comparing HSL vs OKLCH
The difference between HSL and OKLCH becomes strikingly clear when you generate a palette of colors at "the same lightness." In HSL, setting five hues to lightness: 50% produces colors that range from very bright (yellow, green) to quite dark (blue, purple). In OKLCH, setting the same five hues to L: 0.65 produces colors that genuinely look like they belong together at the same brightness level.
Visual Comparison: HSL vs OKLCH at Equal Lightness
This perceptual consistency is not just an aesthetic nicety. It directly impacts design decisions. When your blue and yellow have the same perceived brightness, they can be used interchangeably as accent colors without one overpowering the other. Buttons, badges, and status indicators maintain visual balance regardless of which color is applied.
Practical Benefits of OKLCH
Consistent Color Palettes
To generate a 5-shade palette in OKLCH, simply vary the lightness while keeping chroma and hue constant. The result is a set of shades that progress evenly from light to dark, with no perceptual jumps or unexpected brightness shifts. This is far more reliable than manipulating HSL lightness, where the perceived step between 40% and 50% may be very different from the step between 50% and 60% depending on the hue.
/* OKLCH palette: consistent lightness steps */
--color-100: oklch(0.95 0.05 250); /* very light */
--color-300: oklch(0.80 0.10 250); /* light */
--color-500: oklch(0.65 0.15 250); /* medium */
--color-700: oklch(0.45 0.12 250); /* dark */
--color-900: oklch(0.25 0.08 250); /* very dark */Better Gradients
Gradients in OKLCH avoid the "muddy middle" problem that plagues HSL gradients. When you gradient between two OKLCH colors, the interpolation happens in a perceptually uniform space, producing smooth, natural-looking transitions without unexpected dark or desaturated bands.
/* Gradient that smoothly transitions through OKLCH space */
background: linear-gradient(
in oklch,
oklch(0.65 0.25 30), /* warm red */
oklch(0.65 0.25 150) /* cool green */
);The in oklch keyword in the gradient tells the browser to interpolate in the OKLCH color space. Without it, the browser defaults to sRGB interpolation, which can produce different (often less pleasing) intermediate colors.
Wider Gamut Access
OKLCH is not limited to the sRGB gamut. Modern displays (especially OLED and wide-gamut LCD panels) can render colors beyond sRGB, and OKLCH can represent these colors natively. By using oklch() with high chroma values, you can access the full P3 color gamut available on modern Apple displays, HDR monitors, and an increasing number of Android devices. This means more vivid, saturated colors that are impossible to express in hex or rgb().
Creating Design Tokens with OKLCH
OKLCH is particularly well-suited for design token systems because its three channels map directly to meaningful design decisions:
- Lightness maps to light/dark theme variations and accessibility (ensuring sufficient contrast)
- Chroma maps to emphasis levels (muted vs. vivid)
- Hue maps to semantic meaning (red for danger, green for success, blue for info)
/* Design tokens using OKLCH */
:root {
/* Base hues */
--hue-primary: 265;
--hue-success: 150;
--hue-danger: 25;
/* Generate a full scale from each hue */
--primary-50: oklch(0.97 0.02 var(--hue-primary));
--primary-100: oklch(0.93 0.04 var(--hue-primary));
--primary-200: oklch(0.85 0.08 var(--hue-primary));
--primary-500: oklch(0.60 0.20 var(--hue-primary));
--primary-800: oklch(0.35 0.12 var(--hue-primary));
--primary-950: oklch(0.20 0.06 var(--hue-primary));
}
/* Dark theme: just shift lightness */
[data-theme="dark"] {
--primary-50: oklch(0.15 0.02 var(--hue-primary));
--primary-100: oklch(0.20 0.04 var(--hue-primary));
--primary-500: oklch(0.65 0.20 var(--hue-primary));
--primary-900: oklch(0.90 0.08 var(--hue-primary));
}This approach makes dark mode implementation straightforward: instead of manually picking new colors for every shade, you can systematically invert or shift the lightness values while keeping chroma and hue constant.
OKLAB vs OKLCH: Cartesian vs Polar
OKLCH is actually the polar-coordinate form of the OKLAB color space. OKLAB uses Cartesian coordinates: L (lightness), a (green-red axis), and b (blue-yellow axis). OKLCH re-expresses the same color information in polar coordinates: L (lightness), C (chroma, the distance from the center), and H (hue, the angle).
Think of it like coordinates on a map. OKLAB gives you latitude and longitude (east-west and north-south distances), while OKLCH gives you a compass bearing and a distance. Both describe the same location, but polar coordinates (OKLCH) are more intuitive for humans because "rotate the hue" and "increase the saturation" are natural operations for color manipulation.
In CSS, both oklab() and oklch() are available. For most practical purposes, OKLCH is preferred because its hue channel makes it easy to create color harmonies (complementary, analogous, triadic) and palette variations. OKLAB is useful when you need direct axis manipulation, such as shifting a color along the green-red or blue-yellow dimension.
Browser Support and Fallback Strategies
With OKLCH supported in all modern browsers (Chrome 111+, Firefox 113+, Safari 15.4+, Edge 111+), adoption is practical today. For projects that must support older browsers, use the cascade fallback pattern:
/* Progressive enhancement */
.button {
/* Fallback for older browsers */
background-color: #6366f1;
/* Modern browsers override with oklch */
background-color: oklch(0.55 0.22 265);
}
/* Or use @supports for larger blocks */
@supports (color: oklch(0 0 0)) {
:root {
--brand: oklch(0.55 0.22 265);
}
}The @supports query is useful when you want to conditionally apply an entire set of OKLCH-based tokens, while the cascade fallback is simpler for individual property declarations.
Try It Yourself
Experiment with OKLCH conversions using our Color Converter & Palette Generator. Enter any color in any format and see its OKLCH representation instantly. Adjust the lightness, chroma, and hue values in the visual picker to see how they correspond to perceived changes. Generate palettes using OKLCH harmony rules and export them as CSS variables for your next project.
Further Reading
- OKLCH in CSS — Evil Martians
Andrey Sitnik's comprehensive article on why OKLCH is better than RGB and HSL.
- CSS Color Level 4 — OKLCH
The W3C specification for OKLab and OKLCH color spaces in CSS.
- oklch.com
Interactive OKLCH color picker and converter.