Hex, RGB, HSL, OKLCH: Which to Pick in 2026
Four CSS color formats, four different audiences. This is what each is good at, why OKLCH is the 2026 upgrade, and a practical guide for which to use where.
Every color in CSS is, ultimately, three or four numbers. The format you write them in — #10B981, rgb(16, 185, 129), hsl(161 84% 39%), oklch(69.4% 0.147 162.5) — produces the same color on screen. But each format models color differently, which means each is suited to different work.
This post is the working developer’s guide: what each format is, what it’s optimized for, and which to reach for in 2026.
The four formats at a glance
| Format | Example | Models | Best for |
|---|---|---|---|
| Hex | #10B981 | Compact RGB | Designer handoff, brand colors, copy/paste |
| RGB | rgb(16 185 129) | Per-channel intensity | Channel-level animation, image processing |
| HSL | hsl(161 84% 39%) | Hue + saturation + lightness | Manual color tweaks, traditional palettes |
| OKLCH | oklch(69% 0.147 162) | Perceptually uniform L+C+H | Modern palettes, accessibility, 2026 default |
All four describe the same color. The difference is which axes are easy to think about.
Hex — the universal default
Hex (#RRGGBB) is RGB written in base 16. Two characters per channel, six characters total. It’s the default for designer→developer handoff because:
- Compact. 6 characters. Fits in tweets, JSON, branding briefs.
- Tool-universal. Photoshop, Figma, Sketch, every dev tool, every design tool — all speak hex.
- No format ambiguity.
#10B981means exactly one thing across every implementation.
What hex is bad at:
- No mental model. You can’t tell from
#10B981whether it’s bluish-green or yellowish-green without rendering it. - No incremental adjustment. “Slightly darker” requires knowing which channels to bump. Compare HSL where you’d just decrease lightness.
- No transparency without 8-digit hex.
#10B981FFis 8-digit hex (RGB+alpha) — supported in modern browsers but less common in design tools.
Use hex for: brand colors, design system tokens that don’t need adjustment, anything you’re copying from a design tool.
RGB — per-channel reasoning
rgb(16 185 129) is the same color as #10B981. RGB is decimal hex; the bytes are identical. CSS supports both space- and comma-separated forms, plus an optional alpha:
rgb(16 185 129) /* modern syntax */
rgb(16, 185, 129) /* legacy syntax (still works) */
rgb(16 185 129 / 0.5) /* with alpha */
rgba(16, 185, 129, 0.5) /* legacy with alpha */
What RGB is good at:
- Per-channel manipulation. “Boost the red channel by 30” is straightforward in RGB; awkward in HSL.
- Image processing. Pixel data is RGB. Tools like canvas’s
getImageDatagive you RGB arrays. - Hardware-aligned. Display panels are physical R, G, B subpixels. RGB matches the metal.
What RGB is bad at:
- Same as hex — no perceptual axis.
- Verbose. Three numbers in 0–255 vs six hex chars.
Use RGB for: programmatic color manipulation, image work, animations that change one channel.
HSL — the human-friendly default that isn’t
hsl(hue saturation lightness). Hue is 0–360° (red=0, green=120, blue=240). Saturation and lightness are 0–100%.
What HSL got right:
- Hue is a wheel. “Move toward yellow” is +30° on the hue. Intuitive.
- Saturation is “muted vs vivid”. 0% saturation is grayscale; 100% is fully saturated.
- Lightness is “darker vs lighter.” In theory.
What HSL got wrong: lightness is mathematical, not perceptual.
Pick yellow at HSL 60°, 100%, 50%. Pick blue at HSL 240°, 100%, 50%. Both have lightness 50%. Look at them side by side. The yellow looks dramatically brighter than the blue.
That’s because HSL’s “lightness” is the midpoint of the RGB cube — a mathematical construction, not a model of how eyes work. Yellow has high green-channel intensity (which dominates perceived brightness, see WCAG luminance), so HSL lightness 50% yellow is far brighter than HSL lightness 50% blue.
Real-world consequence: a palette built in HSL with even lightness steps does not feel like even lightness steps. Designers learned to compensate by tuning each hue manually. Twenty years of palette tools papered over this with ad-hoc adjustments.
HSL is still useful for one-off tweaks where you’re eyeballing the result. But for systematic palette generation, you want OKLCH.
OKLCH — the 2026 upgrade
oklch(lightness chroma hue):
- Lightness (0–100%): perceptually uniform. 60% yellow looks the same brightness as 60% blue.
- Chroma (0–~0.4): how saturated. 0 is gray; higher is more vivid.
- Hue (0–360°): same as HSL hue, just in a different color space.
Background: OKLCH is built on the Oklab color space by Björn Ottosson, designed specifically to be perceptually uniform. Equal numerical steps produce equal visual steps. This is what HSL was supposed to do but didn’t.
What OKLCH unlocks:
- Even palette ramps. Set lightness 95%, 85%, 75%, … 15%, 5% for a 10-step shade scale that feels evenly stepped across all hues.
- Accessible defaults. Pick a base color, derive light/dark variants by adjusting L predictably. Contrast ratios become predictable.
- Hue rotation that doesn’t drift. Rotating hue while keeping L and C constant produces colors that feel like the same “weight” across hues. HSL drifts in lightness.
CSS support: every browser shipped after late 2023 supports oklch() natively. As of 2026, that’s safe to use without fallbacks. Tailwind 4 ships with OKLCH by default. Radix Colors uses OKLCH internally. Open Props OKLCH variant is standard.
:root {
--primary: oklch(69% 0.15 162);
--primary-light: oklch(85% 0.10 162); /* same hue, lighter */
--primary-dark: oklch(45% 0.18 162); /* same hue, darker, slightly more saturated */
}
The lightness changes feel even because they are even.
Quick conversion reference
The Color Picker does this live, but if you need to know the math: every browser and CSS engine ships with conversion code. The pipeline:
Hex ↔ RGB (trivial — 2 hex chars per channel)
RGB → linear RGB (gamma decode)
linear RGB → OKLCH (matrix multiply + cube root + atan2)
RGB → HSL (algorithm in CSS spec, doesn't go through linear RGB)
OKLCH conversion is non-trivial — it goes through linearization and matrix transforms. For full implementation see the CSS Color Module 4 spec.
Which format to use, when
A practical decision tree:
| Situation | Format | Why |
|---|---|---|
| Receiving a color from design | Whatever they sent (usually hex) | Don’t round-trip-convert; preserve what they specified |
| Storing brand colors in a token file | Hex | Compact, universal |
| Building a design system shade scale | OKLCH | Perceptual uniformity wins on every axis |
| Animating a single channel | RGB | Direct channel access |
| Manual quick tweak (“a bit darker”) | HSL or OKLCH | Adjust lightness directly |
| Calculating contrast | Doesn’t matter — convert to luminance internally | All formats produce the same luminance |
| CSS custom properties for theming | OKLCH | Easy variants, predictable behavior |
| Anywhere a designer works in Figma | Hex (until Figma fully supports OKLCH) | Round-trip what they hand you |
Three palette gotchas
1. Don’t mix formats in a palette
Don’t define some colors in hex and others in OKLCH within the same token file. Pick one format for the canonical definition, derive everything else. Mixing makes the file unreadable and creates silent rounding drift.
2. Don’t try to math hex
// Don't do this
const darker = `#${(parseInt(hex.slice(1), 16) - 0x101010).toString(16)}`;
That subtracts 16 from each channel — but you can’t tell from looking at hex whether it’ll go negative, overflow, or shift the hue. Always convert to RGB or OKLCH for arithmetic, then convert back.
3. Don’t trust HSL “evenness”
If you’re building a 10-step shade ramp and want it to feel even, use OKLCH. HSL ramps look uneven; you’ll spend hours hand-tuning to fix what’s a mathematical mismatch.
What’s next
Hex isn’t going away. RGB isn’t going away. HSL probably is going away as the default for palette tooling — modern systems standardize on OKLCH. The transition is similar to em → rem in CSS: an existing format that mostly worked, replaced by a slightly better one as browser support caught up.
For 2026 work: default to OKLCH for systematic palettes, hex for handoff, RGB for channel manipulation, HSL for legacy compatibility. That’s the entire decision.
Further reading
- WCAG Contrast Explained (AA vs AAA, When It Matters)
- Color Picker tool — convert between all four formats live
- CSS Color Module Level 4 — the spec
- Björn Ottosson’s Oklab article — the original OKLCH/Oklab paper
Related posts
- WCAG Contrast Explained (AA vs AAA, When It Matters) — Color contrast determines who can read your interface. This is the WCAG math, th…
Related tool
Pick colors, convert hex/RGB/HSL/OKLCH, and check WCAG contrast.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.