CSS supports more color formats than most developers use. Knowing when to reach for hex vs HSL vs oklch affects readability, maintainability, and how easily you can manipulate colors in code.
Hex
The most widely used format. A 6-digit hexadecimal value representing red, green, and blue channels:
color: #3b82f6; /* blue */
color: #ff0000; /* red */
color: #000000; /* black */
color: #ffffff; /* white */
Each pair represents a channel from 00 (0) to ff (255). You can also use 3-digit shorthand when each pair is a repeated digit:
color: #fff; /* same as #ffffff */
color: #000; /* same as #000000 */
color: #f00; /* same as #ff0000 */
With alpha (transparency): append two more hex digits (00–ff):
color: #3b82f680; /* 50% opacity blue */
color: #00000040; /* 25% opacity black */
When to use hex: copying from design tools (Figma, Sketch export hex), existing codebases that use hex, when brevity matters more than readability.
To convert between hex and other formats, the Color Picker shows all representations simultaneously — paste any format and get the others.
RGB
Explicit red, green, blue channels from 0–255:
color: rgb(59, 130, 246); /* blue */
color: rgba(59, 130, 246, 0.5); /* 50% opacity blue */
/* Modern syntax — alpha as optional 4th value */
color: rgb(59 130 246);
color: rgb(59 130 246 / 50%);
color: rgb(59 130 246 / 0.5);
The modern rgb(r g b / alpha) syntax (no commas) is now widely supported and preferred over rgba(). Both work.
When to use RGB: when you need to manipulate individual channels in JavaScript, or when working with color values from an API or sensor data.
// Useful for dynamic colors in JavaScript
const r = 59, g = 130, b = 246;
element.style.color = `rgb(${r} ${g} ${b})`;
HSL
Hue, Saturation, Lightness — the most human-intuitive format:
color: hsl(217, 91%, 60%); /* blue */
color: hsl(217 91% 60%); /* same, modern syntax */
color: hsl(217 91% 60% / 0.5); /* 50% opacity */
- Hue: 0–360, the color wheel position (0=red, 120=green, 240=blue)
- Saturation: 0% (grey) to 100% (fully saturated)
- Lightness: 0% (black) to 100% (white)
Why HSL is better for code:
You can intuitively derive color variations by adjusting a single value:
.button {
background: hsl(217 91% 60%);
}
.button:hover {
background: hsl(217 91% 50%); /* darker — just change lightness */
}
.button:disabled {
background: hsl(217 20% 70%); /* desaturated — just change saturation */
}
Try doing that with hex. You can't, at least not without calculating the new hex value.
When to use HSL: when creating color systems, theme variations, or any time you need to programmatically derive related colors. Also excellent with CSS custom properties:
:root {
--brand-hue: 217;
--brand-saturation: 91%;
}
.primary { color: hsl(var(--brand-hue) var(--brand-saturation) 60%); }
.primary-dark { color: hsl(var(--brand-hue) var(--brand-saturation) 45%); }
.primary-light { color: hsl(var(--brand-hue) var(--brand-saturation) 75%); }
HWB
Hue, Whiteness, Blackness — a newer format (CSS Color Level 4):
color: hwb(217 20% 10%);
Less commonly used but can be more intuitive for mixing colors with white or black.
oklch
The most modern and perceptually accurate format:
color: oklch(65% 0.2 264); /* blue */
- L: lightness 0–1 (or 0%–100%)
- C: chroma (saturation), roughly 0–0.4
- H: hue angle 0–360
Why oklch is better than HSL: HSL's lightness is not perceptually uniform. hsl(60 100% 50%) (yellow) appears much brighter than hsl(240 100% 50%) (blue) despite both having 50% lightness. oklch uses the OKLab color space which is calibrated to human perception — colors at the same lightness value actually look equally bright.
/* These actually look the same lightness in oklch */
.yellow { color: oklch(80% 0.18 105); }
.blue { color: oklch(80% 0.18 264); }
.pink { color: oklch(80% 0.18 350); }
/* In HSL, you'd need to manually adjust each */
Browser support: oklch is supported in all modern browsers (Chrome 111+, Firefox 113+, Safari 15.4+). Not supported in IE.
When to use oklch: new projects targeting modern browsers, design systems requiring perceptual color uniformity, when you want accessible color palettes.
Color accessibility
For text on backgrounds, contrast ratio matters. The WCAG 2.1 standard requires:
- 4.5:1 for normal text (AA)
- 3:1 for large text (AA)
- 7:1 for normal text (AAA)
The Color Contrast Checker calculates the ratio between any two colors and shows WCAG AA/AAA pass/fail — paste hex, RGB, or HSL values directly.
Choosing a format
| Format | Use when |
|---|---|
| hex | Design tool export, existing hex-based codebases, brevity |
| rgb | JavaScript color manipulation, channel-level math |
| hsl | Color variations, themes, readable color systems |
| oklch | Perceptually uniform palettes, modern browsers only |
One practical approach: use hex for static brand colors, HSL for dynamic variations (hover states, dark mode), oklch when you're building a full design system and need perceptual uniformity.
Consistent usage
Whichever format you choose, be consistent. Mixing hex and HSL across a codebase means every developer has to mentally translate between formats. If using CSS custom properties, define all base colors in one format:
:root {
/* All colors defined once, in one format */
--blue-500: hsl(217 91% 60%);
--blue-600: hsl(217 91% 50%);
--green-500: hsl(142 71% 45%);
--red-500: hsl(0 84% 60%);
}
Hex is convenient for copying from design tools. HSL is the most practical for code-driven color systems. oklch is the future for perceptual accuracy. The choice depends on your project's needs — but knowing all three lets you make the call deliberately rather than defaulting to hex out of habit.
Top comments (0)