Hex vs RGB vs HSL vs OKLCH
On this page
TL;DR. Use hex for color literals in CSS. Use HSL when generating palettes by varying lightness or saturation. Use OKLCH when you need perceptually uniform changes (the modern design-system default). RGB is rarely the best choice for human-edited code, but it’s universal in image-processing libraries.
At a glance
| Format | Reads “by channel” | Reads “by perception” | Good for | Used in |
|---|---|---|---|---|
Hex (#2563eb) | Yes (RGB encoded) | No | CSS literals, compact storage | Everywhere |
RGB (rgb(37, 99, 235)) | Yes | No | Image processing, programmatic generation | CSS, image libs |
HSL (hsl(220, 83%, 53%)) | No | Partly | Designing palettes, vary saturation/lightness | CSS, design tools |
| HSV/HSB | No | Partly | Same as HSL but different L mapping | Photoshop, GIMP |
OKLCH (oklch(54% 0.21 263)) | No | Yes | Perceptually-uniform palettes | CSS Color L4, modern design systems |
| LAB / OKLab | No | Yes | Image processing, color science | ICC profiles, OKLab raw |
| CMYK | Yes (subtractive) | No | Adobe |
Same color, four ways
Tailwind blue-600:
hex: #2563eb
rgb: rgb(37, 99, 235)
hsl: hsl(220, 83%, 53%)
oklch: oklch(54% 0.21 263)
All four reference the same sRGB pixel value. The difference is what each makes easy to manipulate.
Hex — the universal default
The most common form. Compact, designer-recognized, supported everywhere.
Use when:
- Specifying a single color literal in CSS or a design tool.
- Storing color in JSON, YAML, or any text format.
- Sharing a color in chat or documentation.
Don’t use when:
- You’re going to programmatically vary the color (use HSL or OKLCH).
- You need an alpha channel — use rgba() or 8-digit hex.
RGB — channels, not perception
Fundamentally the same as hex but spelled out. rgb(37, 99, 235) and
#2563eb describe the same color.
Use when:
- Image processing (every pixel library uses RGB).
- Programmatic color generation by channel manipulation.
- You need alpha (
rgba(37, 99, 235, 0.5)).
Don’t use when:
- It’s just a static literal — hex is shorter.
- You want to design palettes — RGB hides perception.
The big problem with RGB for design: small changes don’t look like
small changes. Increasing red by 20 in rgb(35, 99, 235) creates a
big visual shift; the same numeric change in rgb(120, 99, 235) is
much less noticeable. Color perception is non-linear in RGB space.
HSL — hue, saturation, lightness
A polar-coordinate parameterization of the RGB cube. The same colors, but addressed by:
- H (hue): 0–360°, where 0/360 is red, 120 is green, 240 is blue.
- S (saturation): 0–100%, where 0 is gray and 100 is fully saturated.
- L (lightness): 0–100%, where 0 is black and 100 is white.
hsl(0, 100%, 50%) /* pure red */
hsl(120, 100%, 50%) /* pure green */
hsl(0, 0%, 50%) /* mid gray */
hsl(220, 83%, 53%) /* tooljo brand */
Use when:
- Generating a palette by varying L (e.g. blue-100 through blue-900):
hsl(220, 83%, 90%)tohsl(220, 83%, 15%). - Adjusting saturation across a UI mode toggle.
- Picking colors in code where the math is mental (red = 0°, blue = 240°).
HSL’s gotcha: equal numeric changes in L don’t produce equal
perceived changes. The jump from hsl(50, 100%, 50%) to hsl(50, 100%, 60%)
looks bigger than the same jump at hsl(220, 100%, 50%) to hsl(220, 100%, 60%).
This is why design systems built on HSL palettes have been finicky for
decades.
OKLCH — perceptually uniform
OKLCH was added to CSS Color Module 4 (~2023). It’s a polar representation of the OKLab color space, designed by Björn Ottosson specifically to fix HSL’s perceptual non-uniformity.
- L (lightness): 0–100%, perceptually uniform.
- C (chroma): ~0–0.4, how vivid the color is.
- H (hue): 0–360° degrees, polar position.
oklch(54% 0.21 263) /* tooljo brand */
oklch(80% 0.16 263) /* lighter — looks "lighter" */
oklch(40% 0.16 263) /* darker — looks "darker" */
The key property: equal numeric changes in L produce equal perceived changes in lightness. Same for C. This makes OKLCH ideal for:
- Palette generation. Tints and shades scale predictably.
- Smooth gradients. OKLCH gradients look better than RGB or HSL.
- Accessibility math. Lightness corresponds to luminance more faithfully.
Use when:
- You’re building a design system from scratch in 2026.
- Generating tints (
L = 95%) and shades (L = 15%) of a base color. - Modern CSS targets (Chrome 111+, Safari 15.4+, Firefox 113+).
Don’t use when:
- You need to support older browsers without a fallback.
- You’re storing a single brand color literal (hex is fine for that).
Decision flowchart
Are you specifying a single color in CSS or a design tool?
└── Hex (#2563eb)
Are you doing image processing or per-pixel manipulation?
└── RGB (or its float-version, often called RGB-A or normalized RGB)
Are you generating a palette of related colors?
├── Modern stack with browser support OK?
│ └── OKLCH (perceptually uniform)
└── Need to support older browsers?
└── HSL (varies lightness across a hue, accept perceptual unevenness)
Do you need alpha (transparency)?
├── In a literal? → 8-digit hex (#RRGGBBAA) or rgba()
└── In OKLCH/HSL? → Use the slash syntax: oklch(54% 0.21 263 / 0.5)
Are you printing? Working with brand specifications?
└── CMYK (different topic, ICC profiles required)
Common color-system mistakes
Building a palette in HSL
Tailwind v3 uses HSL for its color scale (blue-50, blue-100, …, blue-900). The result: not all 11 shades feel evenly spaced perceptually. Tailwind v4 switched to OKLCH, and the gradient feels visibly smoother.
If you’re building a new design system in 2026, start in OKLCH.
Mixing color spaces in a single palette
Picking blue-500 from HSL math and blue-600 from a designer’s intuition gives you two colors that don’t relate predictably. Pick one parameterization and use it consistently.
Using RGB component math for “lighter”
rgb(r * 1.2, g * 1.2, b * 1.2) doesn’t make a color “20% lighter.”
It clips bright channels and scales dark channels disproportionately.
Convert to HSL or OKLCH, increment L, convert back.
Browser support (early 2026)
| Format | Chrome | Safari | Firefox |
|---|---|---|---|
| Hex | All | All | All |
rgb() | All | All | All |
hsl() | All | All | All |
rgba() | All | All | All |
hsla() | All | All | All |
| 8-digit hex | All | 12+ | 49+ |
oklch() | 111+ | 15.4+ | 113+ |
oklab() | 111+ | 15.4+ | 113+ |
lab(), lch() | 111+ | 15+ | 113+ |
color-mix() | 111+ | 16.2+ | 113+ |
color() (with display-p3, etc.) | 111+ | 15+ | 113+ |
OKLCH and friends are safe to use on the modern web. For older
browsers, fall back to hex via @supports (color: oklch(54% 0.21 263)).
Try the tools
- Color converter — see all four formats simultaneously
- Hex → RGB — focused converter
- RGB → Hex — focused converter
- Contrast checker — accessibility ratios
- What is a hex color? — fundamentals
Related across the network
- hex.tooljo.com/blog/oklch-for-design-systems — practical guide to using OKLCH for tonal scales, dark themes, and contrast calibration.
- hex.tooljo.com/blog/tailwind-v4-oklch — what changed when Tailwind v4 made OKLCH the default.