Guide
CSS color formats: hex, RGB, HSL, OKLCH — which to use when
Four formats, four use cases. The right pick depends on what you're trying to do with the colour.
Modern CSS supports four primary colour notations: hex (#FF6600), rgb(),hsl(), and the newer oklch(). All describe the same colour gamut (sRGB by default, wider for OKLCH). They differ in human readability, ease of manipulation, and what kind of colour change feels natural.
The four formats at a glance
| Format | Example | Best for |
|---|---|---|
| Hex | #FF6600 | Storing colours, exchanging with designers, code that doesn’t modify the value. |
| rgb() | rgb(255 102 0) | Programmatic generation, when the channel-by-channel values matter. |
| hsl() | hsl(24 100% 50%) | Manually tweaking lightness and saturation while keeping the hue locked. |
| oklch() | oklch(0.72 0.18 47) | Designing colour scales where perceptual uniformity matters. |
Hex: the storage format
Hex encodes each RGB channel as a two-digit hexadecimal value. #FF6600means red 255, green 102, blue 0 — that’s a vivid orange.
Pros: short, universally understood, copy-paste friendly, works in every CSS context.
Cons: opaque to read (“is #7E4F2Ba warm or cool tone?”), can’t easily adjust lightness or saturation without converting first.
Modern CSS supports 8-digit hex for transparency:#FF6600AA is the orange above at 67% opacity (AA = 170 / 255).
rgb(): the explicit channel format
Same channels as hex, decimal numbers, optional alpha. Two syntaxes coexist:
- Legacy comma form:
rgb(255, 102, 0)or with alphargba(255, 102, 0, 0.67) - Modern space form:
rgb(255 102 0)or with alphargb(255 102 0 / 0.67)
Best for programmatic generation: building colour from algorithm output, blending two colours, computing accessibility contrast.
hsl(): the human-tweakable format
Three values: hue (0-360°), saturation(0-100%), lightness (0-100%). Adjusting each independently feels more natural than tweaking RGB channels.
- Hue: the colour itself. 0 = red, 60 = yellow, 120 = green, 180 = cyan, 240 = blue, 300 = magenta.
- Saturation: 0% = grey, 100% = pure colour.
- Lightness: 0% = black, 50% = pure hue, 100% = white.
Best for: design tokens where you want a “same colour, darker” variant — drop the lightness by 10%, keep hue and saturation. Hover states, theme variants, and gradients all benefit.
The catch: HSL’s lightness doesn’t track perceived brightness uniformly. HSL yellow at 50% lightness looks much lighter than HSL blue at 50% lightness. For perceptually-uniform colour design, OKLCH is the better choice.
oklch(): the perceptual format
Three values: L (lightness, 0-1),C (chroma, 0-~0.4 for sRGB-displayable),H (hue, 0-360°). Based on the OKLab perceptual colour space (Björn Ottosson, 2020).
The key property: equal lightness values look equally bright. oklch(0.6 0.2 0) (red) andoklch(0.6 0.2 120) (green) andoklch(0.6 0.2 240) (blue) are all the same perceived brightness, unlike HSL where these would vary dramatically.
Best for: designing colour scales, dark-mode themes, ensuring text contrast holds across a hue rotation. Supported in all evergreen browsers since 2023.
The catch: chroma maxes out at different values per hue. High-chroma blue is achievable; high-chroma yellow runs into sRGB gamut limits earlier. Use a chroma value the browser can render at every hue you need, or accept clipping at the extremes.
Picking the right one
- For brand colours and design tokens:define in HSL or OKLCH; that’s what designers can tweak meaningfully. Store hex as a fallback in legacy files.
- For UI colour ramps (50/100/200/.../900):OKLCH with a fixed chroma and varying lightness produces perceptually-even scales. HSL works but the visual spacing is uneven.
- For copying from a Figma/Photoshop spec:paste hex. Convert via our hex to RGB tool or a CSS preprocessor when you need to manipulate.
- For accessibility-driven choices: compute WCAG contrast on the rgb form. The contrast formula operates on linear RGB, not on hex or HSL directly.
The CSS custom-property pattern
Modern theme architectures store colours as HSL/OKLCH custom properties so they can be derived:
:root {
--primary-hue: 24;
--primary: oklch(0.72 0.18 var(--primary-hue));
--primary-hover: oklch(0.65 0.18 var(--primary-hue));
--primary-bg: oklch(0.92 0.05 var(--primary-hue));
}Changing the hue once rotates the entire palette consistently. The same pattern with hex is impossible without preprocessor functions.
The takeaway
Hex for storage and copy-paste. RGB for programmatic. HSL for designer tweaking. OKLCH for perceptually-uniform ramps. The CSS spec lets you mix freely; pick the format that matches what you’re doing with the colour.
Sources: CSS Color Module Level 4 (W3C); Björn Ottosson,A perceptual colour space(2020); MDN’s Modern CSS Colour Functions documentation.
Related
Published May 16, 2026