sodcalculator

10 Tech How-To

Modern CSS Color Functions: How to Use Them

Guide 10-32468 · Updated · Step by step

Modern CSS color functions like rgb(), hsl(), lab(), and color() let you define colors with more precision and flexibility than ever before. You use them in any CSS property that accepts a color, such as color, background-color, or border-color, by typing the function name, its arguments, and a closing parenthesis. This guide explains the syntax, the differences between each function, and how to choose the right one for your project.

Background: From Hex to Full Color Spaces

For decades, CSS colors were mostly limited to hexadecimal values (#ff0000), named colors (red), and the original rgb() and hsl() functions. These work fine for sRGB, the standard color space for screens, but they cannot represent colors outside that gamut. Modern displays and printers can show a wider range of colors (P3, Rec. 2020), and modern CSS functions were created to access those wider gamuts. The CSS Color Module Level 4 specification introduced new functions and expanded the old ones, giving you more accurate, perceptually uniform, and device-independent color control.

How the Functions Work

Each color function takes specific arguments. The most important ones you will use are:

  • rgb() and rgba(): Red, green, blue channels. Modern syntax allows space-separated values with an optional slash for alpha: rgb(255 0 0 / 0.5). The comma-separated form (rgb(255, 0, 0, 0.5)) also works for backwards compatibility.
  • hsl() and hsla(): Hue (0-360 degrees), saturation (0-100%), lightness (0-100%), and optional alpha. Example: hsl(120 50% 50% / 0.8).
  • lab(): Lightness (0-100), a-axis (green to red), b-axis (blue to yellow). Values can be negative or exceed 100 for wide gamut. Example: lab(50% 40 59.5).
  • lch(): Lightness, chroma (colorfulness), hue. More intuitive than lab() for many. Example: lch(50% 50 30).
  • color(): The most flexible – specify a color space and channel values. Example: color(display-p3 1 0 0) for a pure red in P3.
  • oklch() and oklab(): Perceptually uniform variants of lch() and lab(), often preferred for color manipulation because they avoid unexpected hue shifts.

How to Choose the Right Function

Your choice depends on what you need:

  • Simple, compatible colors: Stick with rgb() or hsl(). They work in all browsers and are easy to read.
  • Wide gamut (P3) colors: Use color(display-p3 ...) or lab()/lch(), which can express colors beyond sRGB.
  • Perceptual uniformity: Use oklch() or oklab() when you need to adjust lightness without distorting hue, like in a dark mode toggle.
  • Reading and maintaining code: hsl() is intuitive for humans (adjust hue for a different shade), while rgb() is closer to how screens work.

Common Mistakes and Misconceptions

One frequent error is mixing old comma syntax with new space syntax – you cannot use rgb(255, 0, 0 / 0.5). Another is forgetting that hsl() hue is in degrees, not radians or percentages. Also, lab() and lch() lightness is a percentage (0-100), but the a and b axes are numbers without units, often ranging from -125 to 125 for sRGB. A common misconception is that color() is only for P3 – it can also specify sRGB, but there is no benefit over rgb() in that case. Finally, remember that wide-gamut colors will be clipped or shifted on devices that do not support them, so always provide a fallback.

Practical Examples

Here are concrete use cases:

  1. Dark mode with oklch(): Instead of manually darkening a hex color, use oklch() and reduce the lightness value. Example: --bg: oklch(0.9 0.05 250) for light mode, --bg: oklch(0.2 0.05 250) for dark mode.
  2. P3 red for a cinematic look: color(display-p3 1 0 0) is more vivid than #ff0000 on capable screens. Use a fallback: background: #ff0000; background: color(display-p3 1 0 0);
  3. Adjusting opacity: Use the slash syntax: rgba(0 0 0 / 0.5) or hsl(0 0% 0% / 0.5). This is shorter than the old rgba(0,0,0,0.5).

Browser Support and Fallbacks

All modern browsers support rgb(), hsl(), and their alpha variants. lab(), lch(), oklab(), oklch(), and color() are supported in Chrome, Edge, Firefox, and Safari as of 2023-2024. For older browsers, use a fallback: declare a hex or rgb() color first, then the modern function. The browser will ignore the modern one if it does not understand it. Example:

.element {
  background: #ff0000; /* fallback */
  background: color(display-p3 1 0 0); /* modern */
}

Always test on your target browsers, especially if you use color() with a custom color space.

Costs and Performance

There is no runtime performance cost to using any of these functions – they are parsed once at render time. The only “cost” is the extra bytes in your CSS file, which are negligible. However, there can be a design cost: wide-gamut colors may look different on different devices, so you need to plan for color management. Also, if you use lab() or lch() without understanding the color space, you might produce colors that look odd. The real cost is in learning the new syntax, not in performance.

Alternatives to Modern Functions

If you do not need wide gamut or perceptual uniformity, you can stick with the classic #hex, rgb(), or hsl(). They are fully supported and easy to write. For design systems, you might also use CSS custom properties to store colors, regardless of the function used. Another alternative is using preprocessors like Sass, which offer their own color functions, but those compile to standard CSS colors. The modern CSS functions are the native way to get the same power without a build step.

Frequently Asked Questions

What is the difference between lab() and lch()?

lab() uses Cartesian coordinates (lightness, a-axis, b-axis) while lch() uses polar coordinates (lightness, chroma, hue). lch() is often easier to reason about because you can adjust hue directly, similar to hsl().

Can I use color() with any color space?

You can use any color space that the browser supports and that is defined in the CSS Color spec, such as srgb, display-p3, a98-rgb, prophoto-rgb, and rec2020. However, not all spaces are supported in all browsers, so check compatibility.

How do I convert a hex color to oklch()?

You can use online converters or browser dev tools. In Chrome DevTools, you can click on a color swatch and change the color format to oklch(). There is no simple mental math – it involves color space conversion.

Why does my lab() color look different from what I expected?

lab() is device-independent, so it should look the same on all screens that can display the color. But if your screen cannot show the full gamut, the color gets clipped. Also, the a and b values are not intuitive – you might need to experiment or use a color picker that shows lab() values.

Is oklch() better than lch()?

For most design work, yes. oklch() is perceptually uniform, meaning that a change in lightness is perceived as the same amount of change regardless of the hue. lch() is not perfectly uniform, so you might see unexpected shifts in perceived brightness when changing hue.

Do I need to use rgba() or can I use rgb() with alpha?

In modern CSS, you can use rgb() with a slash to include alpha: rgb(0 0 0 / 0.5). The rgba() function is an alias that still works, but it is not necessary. The same applies to hsl() and hsla().

Related

General steps for current mainstream versions; menus can differ by device and version. See the disclaimer or report an error.

More in this bed

The whole Tech How-To bed →