Relative Color Syntax â Basic Use Cases
Relative Color Syntax â Basic Use Cases êŽë š
As of last month, Firefox 128âs support of the relative color syntax means weâve now got support across the board. Iâm excited about that as itâs an extremely powerful way to manipulate colors in CSS. Plus it was part of Interop this year so that is further proof that is trucking along nicely.
The syntax with generic names looks like this:
color-function(from origin-color channel1 channel2 channel3 / alpha)
Hereâs how it works in my head:
Add Opacity to a Color you Already Have
Itâs common to have CSS custom properties set up for colors on a project.
html {
--color-yellow: oklch(80% 0.15 94);
--color-green: oklch(70% 0.25 140);
...
}
Now you want to use that yellow, but at about 50% opacity. How do you do that? There are actually a couple of ways to add transparency to an existing color, but in my opinion the relative color syntax is the nicest.
In the past, Iâve split out the color values like this:
html {
--color-yellow-lch: 80% 0.15 94;
--color-yellow: oklch(var(--color-yellow-lch);
...
}
That way I could either use the color all together, or use the split out values to apply opacity:
.box {
background: var(--color-yellow);
border-color: oklch(var(--color-yellow-lch) / 50%);
}
But that can get out of hand! You could also split each color into L, C, and H, the combine those, giving you five variables for every color. Too much.
With the relative color syntax, breaking down colors isnât necessary. You apply alpha (and other transformations) on demand, leaving the original single color as the only variable (token) you need.
.box {
background: var(--color-yellow);
border-color: oklch(from var(--color-yellow) l c h / 50%);
}
I much prefer the idea of keeping the main colors tokenized as custom properties, then tweaking them as needed on demand.
Darken a Color you Already Have
In the above example, we had --color-yellow
and I ended by saying I prefer doing one-off tweaks on demand rather than making a whole new variable. If you have a ton of usage of a slightly-darker version of a color, then sure, make a new variable and stay consistent. But if itâs more of a one-off, relative color syntax is awesome:
.box {
background: var(--gray-5);
h2 {
color: var(--color-yellow);
/* Darkened version of yellow */
border-bottom: 2px solid oklch(from var(--color-yellow) calc(l - 0.4) c h);
}
}
Lighten a Color you Already Have
Same deal here. Iâm using OKLCH because I like it, particularly the âuniform brightnessâ characteristic. Meaning when doing this darkening and lightening across different colors, it feels like it lightens/darkens the same amount. Which feels weird to write, but itâs true. Other color spaces do not lighten and darken consistently.
.box {
background: var(--gray-5);
h2 {
color: var(--color-orange);
/* Darkened version of orange */
border-bottom: 2px solid oklch(from var(--color-orange) calc(l + 0.4) c h);
}
}
Easy Variations
Avoiding making too many variables is a nice consequence of the relative color syntax, but you can still use the relative color syntax to make variables if itâs useful to have them.
I like the idea of starting with a base color, perhaps a slightly tinted gray, and then making the official variations with the relative color syntax.
html {
--base-gray: oklch(12.94% 0.02 159);
--gray-1: var(--base-gray);
--gray-2: oklch(from var(--base-gray) calc(l + 0.1) c h);
--gray-3: oklch(from var(--base-gray) calc(l + 0.2) c h);
--gray-4: oklch(from var(--base-gray) calc(l + 0.3) c h);
--gray-5: oklch(from var(--base-gray) calc(l + 0.4) c h);
--gray-6: oklch(from var(--base-gray) calc(l + 0.5) c h);
--gray-7: oklch(from var(--base-gray) calc(l + 0.6) c h);
--gray-8: oklch(from var(--base-gray) calc(l + 0.7) c h);
}
The fact that you can start with any color, use any color function, and manipulate any part of the color is incredibly powerful. The above use cases are pretty basic. Iâm sure more talented designers or developers who deeply know color will be able to do much more interesting things!