LCH and OKLCH in CSS: Browser Support, Migration from Hex/RGB, and When to Use Each
A practical guide to lch() and oklch() in CSS — the difference between them, current browser support figures, and step-by-step migration patterns using @supports fallbacks.
LCH and OKLCH in CSS: Browser Support, Migration from Hex/RGB, and When to Use Each
When I refactored a design system from hex to OKLCH last spring, the first thing I noticed was not how much the colors changed — most of them barely moved — but how much easier it became to generate tints and shades programmatically. A one-line calculation in the lightness channel now did what three rounds of visual tweaking used to do. That shift in workflow is what the modern CSS color spaces are actually selling, and it is worth understanding both lch() and oklch() to know which one to reach for.
The Problem That Both Spaces Solve
Hex and RGB represent colors as amounts of red, green, and blue light. The format is technically precise and machine-friendly, but it has a core problem: the numbers do not map to human perception. Stepping from #1a1a6e to #2a2a9e by 16 in each channel produces a jump that looks enormous in dark blues but tiny in yellows. Equal numeric steps are not equal visual steps.
HSL improved readability by converting to hue, saturation, and lightness, but it borrowed the underlying sRGB math. Its "lightness" is not actually perceptual. A yellow at hsl(60 100% 50%) looks wildly brighter than a blue at hsl(240 100% 50%) even though both claim 50% lightness. This is why generating a multi-hue design token palette in HSL requires hand-correction at every hue.
LCH and OKLCH both solve this by grounding their lightness channel in how the human visual system works. Colors at the same L value look equally bright across hues — or close enough that the remaining difference is negligible for most design decisions.
LCH vs OKLCH — The Practical Difference
lch() (Lightness, Chroma, Hue) is the cylindrical form of the CIELAB color space, a standard developed in the 1970s by the International Commission on Illumination (CIE). CIELAB was designed to be perceptually uniform, and it mostly is — but "mostly" has a well-documented edge case in blues. As you increase chroma for a blue color in LCH, the hue angle drifts toward purple. If you build a blue brand palette in LCH and crank the saturation for an accent, you get an accent that your eye reads as violet, not the blue you intended.
oklch() corrects this. It uses OKLab, a color space published by Björn Ottosson in 2020, which was fitted to actual perception data to eliminate the hue drift. Blues stay blue as chroma rises. Hue interpolation (critical for gradients and color-mix()) follows a straight arc instead of curving through adjacent hues.
The syntax for both is the same shape:
/* lch: lightness 0-100, chroma 0-150, hue 0-360 */
color: lch(62 48 264);
/* oklch: lightness 0-100%, chroma 0-~0.4, hue 0-360 */
color: oklch(62% 0.19 264);
The chroma scale differs: LCH expresses chroma up to around 150, while OKLCH uses a tighter 0–~0.37 range inside sRGB. Both represent the same physical saturation — just with different unit conventions. The practical takeaway: use oklch() for new work. LCH remains useful when you're reading out of an older tool or library that outputs CIE values, but for authoring, OKLCH is the current recommendation in the CSS Color 4 specification.
Browser Support in 2026
According to Can I Use data from mid-2026, oklch() has 93.4% global browser support and lch() sits at 93.2%. Both landed simultaneously in Chrome 111 (March 2023), Firefox 113 (May 2023), and Safari 15.4 (partial) / 16.4 (full support). The gap between LCH and OKLCH is negligible — any browser that understands one understands the other.
The unsupported 6.6% is almost entirely legacy Android WebView, very old Samsung Internet versions, and a tail of IE-based enterprise browsers. For most consumer-facing web products, both functions are safe to ship without a fallback in 2026. The remaining question is whether your specific users fall into that 6.6%, which requires checking your own analytics.
I tested oklch() against our site's browser distribution — roughly 0.8% of our users landed on unsupported browsers based on User-Agent data from Q1 2026. For that audience, a hex fallback is trivially cheap and correct.
Migrating an Existing Codebase from Hex and RGB
The migration is simpler than it sounds. You do not need to rewrite everything at once, and you do not need a build tool.
Step 1: Convert your design tokens first, not your full stylesheet.
Take your brand primary color — say Tailwind's blue-500, #3b82f6 — and convert it to OKLCH. The exact output: oklch(60.27% 0.2217 259.2). (You can get this instantly with the OKLCH Color Converter tool — paste the hex, copy the OKLCH string.) Convert your five to ten most-used tokens: primary, secondary, error, success, and the neutral grays.
Step 2: Write a CSS custom property pair for each token.
:root {
--color-primary-hex: #3b82f6;
--color-primary: oklch(60.27% 0.2217 259.2);
}
Both variables live in the same :root block. The hex version is your existing code's fallback; the OKLCH version is what modern browsers will use.
Step 3: Apply the cascade fallback pattern in component CSS.
.btn-primary {
background: var(--color-primary-hex); /* legacy */
background: var(--color-primary); /* modern */
}
Browsers that do not understand OKLCH custom property values will ignore the second declaration and keep the first. No @supports block required for this pattern — the cascade does the work.
Step 4 (optional): Use @supports for structural changes that depend on gamut expansion.
If you want to use P3 wide-gamut OKLCH values (chromas above ~0.37, which exceed sRGB) for displays that support them, wrap those rules:
@supports (color: oklch(0 0 0)) and (color: color(display-p3 0 0 1)) {
.hero-bg {
background: oklch(60% 0.32 259); /* vivid P3 blue, out-of-sRGB-gamut */
}
}
This is the point where OKLCH genuinely exceeds what RGB/hex can express — P3 displays, standard since most Apple hardware from 2016 onward, can render colors that #3b82f6 cannot reach.
Removing the Manual Math
The stumbling block for most teams is the conversion step. Checking your entire palette by hand is tedious. The CSS Color Format Converter tool converts hex, RGB, HSL, and OKLCH in bulk and outputs ready-to-paste CSS custom property declarations and Tailwind config values — which cuts the token migration to a copy-paste job rather than a calculation session.
For one-off conversions during design review, the OKLCH Color Converter shows a live preview of the converted color alongside the original, which is useful for confirming that the converted value renders correctly in sRGB before committing it to your codebase.
What to Expect After Migration
After converting Toolora's own token set from hex to OKLCH, generating a tint-shade ramp for any new color became a mechanical operation: fix chroma and hue, step lightness in 10% increments. The resulting ramp was usable on screen without hand-correction. That had never been true with our hex-based workflow.
The browser risk is real but small. A 93.4% baseline with a hex fallback gives you a safe upgrade path today. LCH is stable for teams that already have CIELAB tooling; OKLCH is the cleaner choice for anyone starting fresh. The migration itself — especially with custom property pairs — takes an afternoon for a mid-sized design system, not a sprint.
Made by Toolora · Updated 2026-06-30