X Xerobit

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.

Mian Ali Khalid · · 9 min read
Use the tool
Color Picker
Pick colors, convert hex/RGB/HSL/OKLCH, and check WCAG contrast.
Open Color Picker →

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

FormatExampleModelsBest for
Hex#10B981Compact RGBDesigner handoff, brand colors, copy/paste
RGBrgb(16 185 129)Per-channel intensityChannel-level animation, image processing
HSLhsl(161 84% 39%)Hue + saturation + lightnessManual color tweaks, traditional palettes
OKLCHoklch(69% 0.147 162)Perceptually uniform L+C+HModern 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. #10B981 means exactly one thing across every implementation.

What hex is bad at:

  • No mental model. You can’t tell from #10B981 whether 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. #10B981FF is 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 getImageData give 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:

SituationFormatWhy
Receiving a color from designWhatever they sent (usually hex)Don’t round-trip-convert; preserve what they specified
Storing brand colors in a token fileHexCompact, universal
Building a design system shade scaleOKLCHPerceptual uniformity wins on every axis
Animating a single channelRGBDirect channel access
Manual quick tweak (“a bit darker”)HSL or OKLCHAdjust lightness directly
Calculating contrastDoesn’t matter — convert to luminance internallyAll formats produce the same luminance
CSS custom properties for themingOKLCHEasy variants, predictable behavior
Anywhere a designer works in FigmaHex (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


Related posts

Related tool

Color Picker

Pick colors, convert hex/RGB/HSL/OKLCH, and check WCAG contrast.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.