Reference

Modern CSS Color Formats Reference

CSS now offers more than ten ways to write the same color. The choice matters: some formats are easier to author by hand, some are perceptually uniform, and some unlock wider gamuts than the screen could even show a decade ago.

Last reviewed on 2026-05-09

Why so many formats exist

For most of CSS's history there was one practical color format — hexadecimal — and one alternative — rgb(). Both describe a point inside the sRGB cube, the standard color space for web monitors. They worked because monitors were uniform and the cube was small.

Two things changed. Modern displays can show colors outside sRGB (the wider Display P3 and Rec. 2020 gamuts). And designers grew tired of color systems that behave inconsistently — increasing "lightness" by 10 produces very different-looking jumps depending on the hue. The answer was to import color spaces that have been used in print and color science for decades, and to give CSS a syntax for any of them.

The result: a richer toolbox. The converter on the home page produces the four most common formats (HEX, RGB, HSL, RGBA) for fast everyday work; this page covers everything else.

The classics

Hexadecimal — #RRGGBB

color: #6C5CE7;
color: #6c5ce7e6;   /* with 8-digit alpha */
color: #f00;        /* shorthand expands to #ff0000 */

Compact, ubiquitous, and the lingua franca of design tools. Best for: pasting one specific color out of a mockup. Worst for: reasoning about relationships between colors. The named colors reference on the home page is essentially a hex lookup table.

RGB — rgb()

color: rgb(108, 92, 231);
color: rgb(108 92 231);          /* modern: spaces, no commas */
color: rgb(108 92 231 / 0.4);    /* with alpha via slash */
color: rgb(42% 36% 91%);         /* percentages also valid */

Same color cube as hex, written in decimal. Useful when integrating with code that already speaks 0–255 channels (canvas, image processing). The rgba() function still works for backwards compatibility but the modern rgb(r g b / α) form makes it redundant — and more on alpha is in the RGBA & transparency guide.

HSL — hsl()

color: hsl(245, 75%, 63%);
color: hsl(245 75% 63%);
color: hsl(245deg 75% 63% / 0.4);

Hue (degrees), saturation (%), lightness (%). Far easier than hex for adjusting brightness or building a scale. Not perceptually uniform — the HSL guide explains the trade-offs.

HWB — hwb()

color: hwb(245 36% 9%);   /* hue, whiteness, blackness */

A lesser-known cousin of HSL: hue plus the percentage of white and the percentage of black mixed in. Some artists find it more intuitive than HSL for tints and shades because adding white is literally adding white. Browser support is now broad. Niche choice — useful when you're thinking like a painter.

Perceptually uniform formats

Lab — lab()

color: lab(56% 38 -64);

Lightness (0–100%), a axis (green to red), b axis (blue to yellow). A perceptual color space derived from CIELAB. Equal numeric distances in lab() correspond approximately to equal perceived differences — the property HSL lacks. Best for color comparisons and scientific work, but rarely used by hand because the a/b axes aren't intuitive.

LCH — lch()

color: lch(56% 74 300);   /* lightness, chroma, hue */

The polar form of Lab. Same perceptual basis, but expressed as lightness + chroma (saturation analogue) + hue (degrees) — much closer to how designers think. Better than HSL for color systems where steps need to feel even.

OKLab and OKLCH — oklab(), oklch()

color: oklch(58% 0.2 280);
color: oklab(58% 0.1 -0.18);

Refinements of Lab and LCH that fix known issues with hue uniformity at the blue end of the spectrum. oklch() is the format design-system maintainers reach for in 2026 — perceptually uniform, intuitive axes, and supported across every modern browser. If you're building a color scale for a serious product, this is the default to pick.

The color() function

color: color(display-p3 0.42 0.36 0.91);
color: color(rec2020 0.42 0.36 0.91);
color: color(srgb 0.42 0.36 0.91);

The escape hatch into wider color spaces. color() takes an explicit color-space identifier and channel values. Wide-gamut spaces (display-p3, rec2020) can express vivid greens and reds that sRGB-only formats cannot. On a monitor that supports them, the difference is real; on an sRGB-only monitor the browser falls back to the closest in-gamut match.

Named colors and keywords

color: rebeccapurple;
color: transparent;
color: currentColor;

The 140+ named CSS colors are documented and clickable on the home page's CSS named colors reference. A handful of keywords are not part of the named-color list but behave like colors:

Choosing a format

If you're…PickWhy
Pasting one color out of FigmaHEXUniversal, terse, exactly what design tools export.
Tweaking brightness or building a small scale by handHSLLightness slider you can adjust in your head.
Building a serious design-system color scaleOKLCHPerceptually uniform; even-looking steps.
Adding a translucent overlayRGBA or 8-digit hexDirect alpha control; see the RGBA guide.
Targeting wide-gamut displayscolor(display-p3 …)Only way to express colors outside sRGB.
Inheriting from the surrounding text colorcurrentColorAdapts automatically to theme changes.
Matching the user's system themeSystem color keywordsBrowser handles light/dark switching.

Modern syntax conventions

Across all the function-style formats, modern CSS prefers spaces over commas, and a slash separator for alpha:

/* old, still valid */
rgb(108, 92, 231)
rgba(108, 92, 231, 0.4)
hsl(245, 75%, 63%)
hsla(245, 75%, 63%, 0.4)

/* modern */
rgb(108 92 231)
rgb(108 92 231 / 0.4)
hsl(245 75% 63%)
hsl(245 75% 63% / 0.4)

Pick one style per codebase. Mixing them works but reads like inconsistent code.

Color-mix and relative color syntax

/* mix two colors */
background: color-mix(in oklch, #6C5CE7 60%, white);

/* derive a color from another */
border-color: hsl(from var(--brand) h s calc(l - 12%));

color-mix() blends two colors in a chosen color space, removing a common reason to commit pre-mixed tints to the codebase. Relative color syntax (hsl(from … ), oklch(from … )) takes an existing color and tweaks one channel — perfect for hover states and theming. Both are now widely supported and especially useful when paired with CSS custom properties.

Gamut, fallbacks, and graceful degradation

Wide-gamut formats can describe colors a sRGB-only monitor cannot show. The browser handles this by mapping the requested color into the destination gamut. The result is usually visually close, but if you depend on the exact wide-gamut value, supply a fallback:

.brand {
  color: #6C5CE7;                                /* sRGB fallback */
  color: color(display-p3 0.42 0.36 0.91);       /* wider gamut */
}

Browsers that don't recognise the second declaration ignore it and use the first. Browsers that do recognise it apply the wider color where the display can show it.

A common-mistakes section

Quick reference