Pro Tips on Using Colors in CSS
Use OKLCH color mode
A little bit of history
First, there was the CIELAB (“Lab”) perceptually uniform color space. Then in 2020, Björn Ottosson created Oklab (“an OK Lab color space”) to address Lab’s problems when used with modern displays. But Oklab uses Cartesian coordinates which is great if you’re a computer, but pretty cryptic otherwise… So OKLCH was added to the mix soon after, using polar coordinates, which makes it much easier to use (similar to HSL) for non-robots.
OKLCH syntax
.elem {
background-color: oklch(0.59 0.13 242); /* blue */
}
- Lightness controls how light or dark the color is. From
0–1(or0%–100%). - Chroma is similar to saturation, but different: it’s how a color looks compared to a neutral gray of the same brightness. A “safe” range is
0–0.35, Values above ~0.37will be clipped on most monitors. - Hue controls what part of the color wheel to sample. From
0deg–360deg
Why use OKLCH
OKLCH is a newer color model designed to be perceptually uniform. Colors are more accurate in terms of how humans perceive them and it makes working with them much easier.
Design system palettes
You can create design system palettes using the same hue while tweaking lightness and chroma:
.example {
--color-1: oklch(0.849 0.107 252);
--color-2: oklch(0.682 0.176 252);
--color-3: oklch(0.629 0.187 252);
--color-4: oklch(0.579 0.191 252);
--color-5: oklch(0.493 0.172 252);
--color-6: oklch(0.425 0.152 252);
--color-7: oklch(0.374 0.131 252);
--color-8: oklch(0.328 0.107 252);
}
Accessibility
This “perceptually uniform” business makes it the best option for a11y friendly palettes. Here’s a comparison between HSL and OKLCH that makes it clear why. We’re only changing the Hue angle by the same amount in both. (Switch to dark mode to make it even more evident.)
.example {
--oklch-1: oklch(0.628 0.2577 29.23);
--oklch-2: oklch(0.628 0.2577 149.23);
--oklch-3: oklch(0.628 0.2577 249.23);
--oklch-4: oklch(0.628 0.2577 319.23);
--hsl-1: hsl(0 100% 50%);
--hsl-2: hsl(120 100% 50%);
--hsl-3: hsl(220 100% 50%);
--hsl-4: hsl(290 100% 50%);
}
Wide gamut P3 colors
OKLCH works with wide-gamut colors, and your browser will render the closest approximation if your screen can’t display it. (Make sure you look at the swatches below on a monitors with P3 color support.)
Intuitive to use
OKLCH mental model is similar to LCH (but with perceptually uniform results): pick a Hue, then adjust the Lightness/Chroma (or vice versa). Below is an OKLCH color picker so you can get a feel for how it works. (The “Gamut correction” warning tells you when the color is out of range for your display.)
.elem {
oklch(0.59 0.13 242.00)
} Use Oklab for gradients
RGB is notorious for interpolating colors with the artistic flair of a vacuum cleaner. OKLCH is great, but it can lead to some unexpected results with gradients because of its polar coordinates underpinnings. Most of the time, Oklab will get you the best results.
.srgb {
background: linear-gradient(90deg, #ff00ff, #00ff00);
}
.oklab {
background: linear-gradient(in oklab 90deg, #ff00ff, #00ff00);
}
.oklch {
background: linear-gradient(in oklch 90deg, #ff00ff, #00ff00);
}
Use the newer/cleaner syntax
For a cleaner syntax, you can drop the commas and units, and add the alpha channel after a /. No need for rgba() anymore!
/* red at 0.5 opacity */
.elem {
oklch(0.628 0.2577 29.23 / 0.5)
}
.elem {
color: hsl(0 100 50 / 0.5);
}
.elem {
color: rgb(255 0 0 / 0.5);
}
Bonus: you can add the alpha channel to Hex colors too:
.elem {
/* you can pass alpha channel at the end of hex too*/
/* (hex 80 = decimal 50) */
background: #ff000080;
}
Use relative colors
Relative colors let you create a color from another color.
:root { --base: hotpink; }
.square-1 { background: hsl(from var(--base) h s 80%); }
.square-2 { background: hsl(from var(--base) h s 70%); }
.square-3 { background: hsl(from var(--base) h s 60%); }
.square-4 { background: hsl(from var(--base) h s 50%); }
.square-5 { background: hsl(from var(--base) h s 40%); }
.square-6 { background: hsl(from var(--base) h s 30%); }
.square-7 { background: hsl(from var(--base) h s 20%); }
.square-8 { background: hsl(from var(--base) h s 10%); }
You can also adjust a custom property’s alpha using relative color syntax:
.elem {
background: oklab(from var(--my-color) l a b / 20%);
}
Resources
If you want more, here are some great resources, including deep dives in the nitty gritty details, as well as some useful tools.