Vorinda
Guides··11 min read

HEX vs RGB vs HSL vs OKLCH: CSS Color Formats Guide

HEX, RGB, HSL or OKLCH? A developer guide to CSS color formats — what each does, when to use it, and how to convert between them.

Hasan Afzal
color formatscsshexrgbhsl
On this page

Ask three front-end developers how to write a color and you will hear HEX, RGB and HSL — and increasingly a fourth answer, OKLCH. The hex vs RGB vs HSL question is rarely about what is possible, because the first three describe the exact same sRGB colors; it is about which notation makes your job easier. This guide covers all four CSS color formats: what each one encodes, when to reach for it, and how to convert between them by hand and with a tool.

The same color, four ways: how sRGB formats relate

HEX, RGB and HSL are not different colors — they are three ways of writing the same point inside the sRGB color space, the standard gamut every monitor has rendered for decades. HEX and RGB both spell out the red, green and blue channels directly; HSL re-describes that same point using hue, saturation and lightness. Because they share one space, converting between them is lossless apart from rounding.

OKLCH is the outlier. It is built on a perceptual model rather than raw RGB channels, and it can address colors beyond sRGB on wide-gamut displays. For colors that exist in sRGB it is still an exact equivalent — it just gets there through different math.

Here is one color, Tailwind's blue-500, written in all four formats:

Format#3B82F6 written as
HEX#3B82F6
RGBrgb(59, 130, 246)
HSLhsl(217.2, 91.2%, 59.8%)
OKLCHoklch(0.623 0.188 259.81)

Same pixel on screen, four notations. The rest of this guide is about choosing the most ergonomic one for each task.

HEX — the default shorthand

A HEX code packs the red, green and blue channels into base-16 (hexadecimal) digits, prefixed with #. In #3B82F6, 3B is red, 82 is green and F6 is blue, each a value from 00 to FF (0 to 255 in decimal). It is the format design tools hand you and the one you will paste most often.

3-, 6- and 8-digit HEX

HEX comes in a few lengths, and knowing them saves real keystrokes:

  • 6-digit (#RRGGBB) — the standard form, one byte per channel.
  • 3-digit (#RGB) — a shorthand where each digit is doubled. #39F expands to #3399FF, #F00 to #FF0000. It only works when both digits of each channel match, so #3B82F6 has no short form.
  • 8-digit (#RRGGBBAA) — adds an alpha (opacity) byte. #3B82F6CC is the same blue at 80% opacity. A 4-digit #RGBA shorthand exists too.

The alpha byte trips people up because it is also hexadecimal. These are the common reference points:

OpacityAlpha byte
100%FF
80%CC
50%80
25%40
0%00

When to use HEX

Reach for HEX for static, fixed values: brand colors, design-handoff tokens, anywhere you just need to drop a value into CSS. It is compact and universally supported, down to the oldest browsers and email clients.

Its weakness is that it is opaque to humans. You cannot look at #3B82F6 and know it is a fairly light, saturated blue, and you certainly cannot nudge it lighter without a tool. When you need a precise value from a screenshot or mockup, sample it with the color picker rather than eyeballing the digits.

RGB / RGBA — code, canvas and transparency

rgb(59, 130, 246) writes the same three channels as decimal numbers from 0 to 255. This is the format programmatic code expects: the Canvas API, WebGL shaders, image-processing libraries and most color utilities all think in RGB channels, so it is the natural choice when you are computing colors in JavaScript rather than hard-coding them.

RGB also handles transparency. The classic rgba() form adds a fourth value from 0 to 1:

.overlay {
  background: rgba(59, 130, 246, 0.8); /* 80% opaque blue */
}

Modern CSS unifies rgb() and rgba() and accepts a slash for alpha, so all of these are valid and equivalent:

color: rgb(59, 130, 246);        /* legacy comma syntax */
color: rgb(59 130 246);          /* modern space syntax */
color: rgb(59 130 246 / 80%);    /* with alpha */

RGB is more readable than HEX — you can at least see that blue dominates here — but it is still poor for adjusting a color. Bumping brightness means changing all three numbers in proportion, which is guesswork. That is HSL's job.

HSL / HSLA — adjusting lightness and saturation by hand

hsl(217, 91%, 60%) describes a color as three human-friendly dials:

  • Hue — an angle from 0 to 360° around the color wheel (0° red, 120° green, 240° blue).
  • Saturation — 0% gray to 100% full color.
  • Lightness — 0% black to 100% white, with the pure hue at 50%.

This is the format to think in when you are deriving colors from a base. Each common UI variation becomes a one-number change:

:root {
  --brand:    hsl(217, 91%, 60%);  /* base */
  --hover:    hsl(217, 91%, 50%);  /* lower lightness  -> #0B64F4 */
  --tint:     hsl(217, 91%, 90%);  /* raise lightness  -> #CEE0FD */
  --disabled: hsl(217, 30%, 60%);  /* drop saturation  -> #7A92B8 */
}

Try expressing those three relationships in HEX and you would be reaching for a converter on every line. HSL also supports alpha the same way RGB does: hsl(217 91% 60% / 80%) or the legacy hsla(217, 91%, 60%, 0.8).

HSL is brilliant for hand-tuning and for generating harmonies — rotate the hue 180° and you have a complement, 120° apart and you have a triad. If you are building schemes this way, the color schemes explained guide shows the angles, and the shade and tint generator automates the lightness ramp.

HSL has one real flaw, though: it is not perceptually uniform. Yellow at 50% lightness looks far brighter to the eye than blue at 50% lightness, so evenly-spaced HSL ramps look lumpy in the mid-tones. That is exactly the gap OKLCH closes.

OKLCH — the modern, perceptually uniform option

oklch(0.623 0.188 259.81) is the newest CSS color format, introduced in CSS Color 4. It uses three components that look like HSL but behave better:

  • L — perceptual lightness, 0 (black) to 1 (white). You can also write it as a percentage, e.g. 62.3%.
  • C — chroma, roughly saturation. It starts at 0 and runs to about 0.4 for the most vivid sRGB colors.
  • H — hue angle, 0 to 360°, like HSL.

The headline feature is that equal steps in L look equal. Where an HSL ramp bunches up and your 400 and 500 shades look almost identical, an OKLCH ramp spaces out smoothly. That makes it the right tool for generating 50950 UI scales and for flipping lightness in dark mode without the hue drifting. OKLCH can also describe colors outside sRGB, so it unlocks wider Display-P3 gamuts on capable screens.

Every current browser — Chrome, Edge, Safari and Firefox — has supported oklch() since 2023. For older clients, list a HEX fallback first; a browser that cannot parse the oklch() line simply ignores it and keeps the previous declaration:

.button {
  background: #3b82f6;                 /* fallback */
  background: oklch(0.62 0.19 259.81); /* modern browsers */
}

If you want the full tour — why "Oklab" exists, how chroma clips at the gamut edge, and where it beats HSL in practice — read what is OKLCH.

Quick comparison table

FormatExampleBest forReadable?Alpha?Gamut
HEX#3B82F6brand values, design handoff, pastingNo8-digit onlysRGB
RGBrgb(59, 130, 246)code, canvas, JS, transparencySomewhatYessRGB
HSLhsl(217, 91%, 60%)hand-tuning lightness & saturationYesYessRGB
OKLCHoklch(0.623 0.188 259.81)UI scales, theming, wide gamutYesYeswide (P3+)

How to convert between them

Because HEX, RGB and HSL share the sRGB space, converting is exact apart from rounding. OKLCH involves a heavier matrix transform, so for that one you will almost always want a tool. Here is the math for the everyday cases, then a full worked example.

HEX to RGB

Split the six digits into three pairs and convert each from base 16 to base 10. Each hex digit is worth its value times a power of 16:

3B = (3 x 16) + 11 = 59
82 = (8 x 16) + 2  = 130
F6 = (15 x 16) + 6 = 246

So #3B82F6 is rgb(59, 130, 246). (Remember AF stand for 10–15.)

RGB to HSL

Normalize each channel to 0–1, then find the largest and smallest:

  1. Lightness is the midpoint: L = (max + min) / 2.
  2. Saturation depends on the spread Δ = max − min: S = Δ / (1 − |2L − 1|).
  3. Hue comes from which channel is the max, scaled by Δ.

A worked example

Let's run #3B82F6 all the way through.

Step 1 — HEX to RGB: as above, rgb(59, 130, 246).

Step 2 — RGB to HSL. Normalize: r = 0.231, g = 0.510, b = 0.965. The max is blue (0.965), the min is red (0.231), so Δ = 0.733.

L = (0.965 + 0.231) / 2 = 0.598          -> 59.8%
S = 0.733 / (1 − |2(0.598) − 1|) = 0.912 -> 91.2%
H = 60 x (4 + (0.231 − 0.510) / 0.733)   = 217.2°

That gives hsl(217.2, 91.2%, 59.8%) — matching the table at the top.

Step 3 — to OKLCH: the transform runs sRGB through linear-light RGB and the Oklab matrices, which is not something you do on paper. The answer is oklch(0.623 0.188 259.81).

For day-to-day work, skip the arithmetic: paste any value into the color format converter and copy HEX, RGB, HSL, OKLCH or CMYK side by side. It is the fastest path for directional conversions like HEX to RGB or RGB to HSL.

Which format should you use?

There is no single winner — pick by the task in front of you.

TaskReach for
Pasting a brand value or design tokenHEX
Computing colors in JS / canvas / shadersRGB
Overlays and transparencyrgba() or 8-digit HEX
Hover, active and disabled statesHSL
Generating a 50950 UI scaleOKLCH
Flipping lightness for dark modeOKLCH
Targeting wide-gamut (P3) screensOKLCH
Maximum legacy browser supportHEX or RGB

A practical default for a modern project: store fixed values as HEX, derive interactive states with HSL, and build any generated ramp or theme in OKLCH with a HEX fallback. Whichever you choose, confirm text and background still pass WCAG with the color contrast checker — format has no effect on contrast, but it is the step people skip. The accessible color contrast guide covers the thresholds in depth, and the blue color page shows our worked-example hue across every format and shade.

Frequently asked questions

What's the difference between HEX and RGB?

HEX and RGB describe the exact same sRGB colors. HEX writes the three channels as six base-16 digits like #3B82F6, while RGB writes them as decimals from 0 to 255 like rgb(59, 130, 246). HEX is more compact; RGB reads more naturally and supports an alpha channel.

Is HSL better than HEX?

Neither is better overall — they suit different jobs. HSL wins when you need to adjust a color by hand, because hue, saturation and lightness are each a single number you can nudge. HEX wins for compact, copy-paste static values like brand colors.

How do I convert HEX to RGB?

Split the six-digit HEX into three pairs and convert each pair from base 16 to base 10. For #3B82F6, 3B = 59, 82 = 130 and F6 = 246, giving rgb(59, 130, 246). A format converter does this instantly for any value.

Which color format is best for CSS?

Use HEX or RGB for fixed values, HSL when you tweak colors by hand, and OKLCH for anything you generate or theme. OKLCH is the strongest default for color scales and dark mode because its lightness is perceptually uniform.

Do HEX, RGB and HSL describe the same colors?

Yes. HEX, RGB and HSL are three notations for the same sRGB color space, so any color in one has an exact equivalent in the others. OKLCH is a different, larger model that can also describe colors beyond sRGB.