Skip to main content
TF
By Rohit V.9 min readArticle

HEX, RGB, HSL, and OKLCH — CSS Color Formats

TF
ToolsFuel Team
Web development tools & tips
Colorful paint swatches arranged in a gradient

Photo by Unsplash on Unsplash

Four Ways to Spell a Color

> Quick answer: HEX and RGB are the same sRGB color in different notation — `#FF5733` is just `rgb(255 87 51)` written in base 16. HSL re-expresses that color as hue, saturation, and lightness, which makes hand-tweaking tints and shades easy. OKLCH is the newer perceptually-uniform space (lightness, chroma, hue) where equal numeric changes produce equal *perceived* changes — so color scales and gradients come out even instead of muddy. Use HEX/RGB for fixed brand values and copy-paste from design tools, HSL when you want to nudge lightness by hand, and OKLCH for palettes, gradients, and theming where perceptual evenness matters. OKLCH ships in Chrome 111+, Safari 16.4+, and Firefox 113+, so it's broadly usable in 2026.

I ignored color formats for years and just pasted hex codes from Figma. Then I tried to build a programmatic color scale in HSL and the mid-tones came out weirdly dark and dull, and I finally had to understand what was actually going on. Turns out the format you pick matters a lot the moment you start *generating* colors instead of just copying them.


Here's each format, what it's actually doing under the hood, the gotcha that bites people (HSL's fake lightness), and which one to reach for in which situation.

HEX and RGB — The Same Thing, Twice

Let's clear up the most common confusion first: HEX and RGB are not different color models. They're the same sRGB color written two ways.

RGB describes a color as three channels — red, green, blue — each from 0 to 255. `rgb(255 87 51)` means full red, a bit of green, a bit of blue, which your eye reads as a warm orange. Add a fourth value for alpha (transparency): `rgb(255 87 51 / 0.5)` is that orange at 50% opacity.

HEX is the exact same numbers in hexadecimal. `#FF5733` breaks into `FF` (255 red), `57` (87 green), `33` (51 blue). Two hex digits map perfectly to one byte (0-255), which is why the format exists — it's compact and copy-pastes cleanly. `#FF5733` and `rgb(255 87 51)` render identical pixels.

So why two notations? HEX is shorter and is what design tools and CSS frameworks spit out, so it's everywhere. RGB is more readable when you want to see the actual channel values or compute with them, and the `rgb(... / alpha)` syntax is a clean way to add transparency. I keep an
online color picker handy precisely because converting between these by hand is tedious and error-prone.

The limitation of both: they're terrible for *manipulating* a color. If you have `#FF5733` and want it 20% lighter, there's no obvious math — you'd have to convert to another space, adjust, and convert back. That's exactly the problem HSL and OKLCH solve.


A few notation details that come up. HEX has shorthand: `#FFF` is the same as `#FFFFFF` because each digit doubles, so it only works when both digits of each channel match. There's also 8-digit HEX (`#FF5733CC`) where the last pair is alpha — handy but less readable than the `rgb(... / alpha)` form. And the modern CSS `rgb()` syntax dropped the commas; `rgb(255 87 51)` and the old `rgb(255, 87, 51)` both work, but the space-separated version is what you'll see in newer code and what pairs cleanly with the `/ alpha` slash. None of this changes the underlying color — it's all sRGB — but knowing the shorthands saves you from second-guessing whether `#F53` is valid (it is). The thing to internalize is that HEX and RGB are a *storage and transport* format for an exact color, not a format you reason about. The moment you need to reason — lighter, darker, more muted — you're reaching for one of the next two.

HSL — Human-Friendly, With a Catch

Color wheel and design swatches on a designer's desk

Photo by Unsplash on Unsplash

HSL re-expresses an RGB color in terms humans think in: Hue (the color, as an angle 0-360° around the wheel), Saturation (how vivid, 0-100%), and Lightness (how light or dark, 0-100%). `hsl(9 100% 60%)` is roughly that same orange.

The appeal is obvious. Want a lighter version? Bump the lightness. Want a muted version? Drop the saturation. Want a complementary color? Add 180 to the hue. For hand-tweaking, HSL is far more intuitive than nudging three RGB channels and hoping.


But here's the catch that cost me an afternoon:
HSL's lightness lies. It's not perceptual. `hsl(60 100% 50%)` (yellow) and `hsl(240 100% 50%)` (blue) both claim 50% lightness, but the yellow is blindingly bright and the blue is dark and deep. Your eyes see wildly different brightness despite identical lightness numbers.

Why does that matter? The moment you build a color scale or interpolate a gradient in HSL, the steps look uneven — bright and washed-out in the yellows, muddy and dark in the blues and purples. If you've ever generated a palette programmatically and wondered why the middle steps looked off, this is why. HSL is great for one-off manual adjustments but unreliable for systematic, generated color work. Josh Comeau's
deep dive on CSS color formats demonstrates this unevenness with interactive examples if you want to see it firsthand.

OKLCH — The Perceptually-Even One

OKLCH fixes HSL's core problem. It's a perceptually-uniform color space, meaning equal numeric changes produce equal *perceived* changes. Its three axes are:

-
L — perceptual lightness (0 to 1, or 0% to 100%). Unlike HSL, this actually tracks how bright the color looks to your eye. - C — chroma, roughly how vivid the color is. It's technically unbounded but practically ranges 0 to about 0.4. - H — hue angle, 0 to 360°, same idea as HSL's hue.

So `oklch(0.7 0.18 35)` is a perceptually-controlled orange. The magic is that if you take a base color and step the L value down evenly, you get a color scale where each step *looks* equally darker — no muddy mid-tones, no blown-out highlights. The same goes for gradients: interpolating in OKLCH avoids the gray dead-zone you often get interpolating in RGB.


This is why design systems are moving to it. Many now store the canonical color token in OKLCH and emit HEX at build time for legacy consumers. When you generate variations programmatically — lighter, darker, accessible-contrast — authoring in OKLCH gives visibly more even results than HSL ever could. The
Evil Martians write-up on OKLCH is the piece that convinced a lot of people, including me, to switch for generated palettes.

Browser support landed back in 2023 — Chrome 111, Safari 16.4, Firefox 113 — so by 2026 you can use it directly in CSS without a fallback for the vast majority of users. For brand-critical fixed values you might still keep a HEX fallback, but for theming and scales, OKLCH is the better tool.

So Which One Do I Use?

Developer working on a CSS stylesheet with color values

Photo by Unsplash on Unsplash

After all that, the practical decision is simple. I pick based on what I'm doing, not on which format is "best" in the abstract — they all describe colors, they just have different ergonomics.

Use HEX or RGB when: you're copy-pasting a fixed value from a design tool, setting a brand color that never changes, or you just want the canonical sRGB value. This is 80% of everyday CSS. `#1a73e8` for your brand blue and move on.

Use HSL when: you want to hand-adjust an existing color — a hover state that's 10% darker, a muted variant with less saturation. For quick manual tweaks where you're eyeballing the result, HSL's intuitive axes make it fast. Just don't trust its lightness for systematic scales.

Use OKLCH when: you're generating colors programmatically — building a 9-step palette, creating an accessible-contrast variant, interpolating a smooth gradient, or theming a design system. Its perceptual uniformity is exactly what these tasks need, and it's the format I now author tokens in.

A workflow that's served me well: design in HEX (because tools output it), store theme tokens in OKLCH (for even generation), and let the build convert to HEX/RGB fallbacks. You get the best of each.


When I need to grab a color off a screen, sample an image, or convert between these formats quickly, I use a dedicated
color picker tool rather than doing the math by hand — it outputs HEX, RGB, and HSL together, which saves a lot of fiddling. The full ToolsFuel tools directory has the rest of the CSS and conversion utilities I keep open while building a palette.

There's one more dimension worth flagging that pure RGB/HSL miss entirely: gamut. sRGB — which HEX, RGB, and HSL all describe — can't represent the vivid colors a modern wide-gamut (P3) display can show. That's where OKLCH (and its sibling `oklab`) earns extra points, because it can address colors beyond sRGB, letting you tap into the punchier reds and greens of a P3 screen with a graceful fallback for older displays. If you're building something where color vibrancy is a selling point — a brand site, a design portfolio — that's a real reason to author in OKLCH and let the browser clamp to sRGB where needed. For most everyday work it won't matter, but it's the kind of headroom that makes the newer space the safer long-term default. Pick the format that fits the task and the colors stop fighting you.

Frequently Asked Questions

What's the difference between HEX and RGB?

There isn't one, really — they're the same sRGB color in different notation. HEX writes the red, green, and blue channels in hexadecimal (#FF5733), while RGB writes them in decimal 0-255 (rgb(255 87 51)). Two hex digits map exactly to one 0-255 byte, so #FF5733 and rgb(255 87 51) render identical pixels. To convert between them quickly, use a [color picker tool](/tools/color-picker).

Why does HSL lightness not match what I see?

Because HSL's lightness isn't perceptual. hsl(60 100% 50%) (yellow) and hsl(240 100% 50%) (blue) both claim 50% lightness, but the yellow looks far brighter than the blue. This means color scales and gradients built in HSL come out uneven — washed out in the yellows, muddy in the blues. For systematic, generated color work, OKLCH is the more reliable choice because its lightness actually tracks perceived brightness.

When should I use OKLCH instead of HSL?

Use OKLCH whenever you're generating colors programmatically — building palettes, creating accessible-contrast variants, or interpolating gradients. Its perceptual uniformity means equal numeric steps produce equal perceived changes, so scales look even and gradients avoid the gray dead-zone. HSL is fine for quick one-off manual tweaks, but OKLCH is the better tool for design systems and any systematic color generation.

Is OKLCH supported in browsers in 2026?

Yes, broadly. OKLCH landed in Chrome 111, Safari 16.4, and Firefox 113 back in 2023, so by 2026 it works for the vast majority of users without a fallback. For brand-critical fixed values you might still keep a HEX fallback, but for theming, palettes, and gradients you can author directly in OKLCH. Many design systems now store tokens in OKLCH and emit HEX only for legacy consumers.

Which CSS color format should I use by default?

For everyday fixed values — brand colors, copy-pasting from design tools — HEX or RGB is the practical default, covering most CSS you write. Switch to HSL when you want to hand-adjust an existing color, and to OKLCH when generating colors systematically. A solid workflow is to design in HEX, store theme tokens in OKLCH, and convert to fallbacks at build time. A developer-focused [online color picker](/blog/online-color-picker-for-developers-hex-rgb-aarrggbb-2026) helps when you're juggling all three formats.

Try ToolsFuel

23+ free online tools for developers, designers, and everyone. No signup required.

Browse All Tools