DEV Community

Fabio Giolito
Fabio Giolito

Posted on

Create a color theme with these upcoming CSS features

Safari Technology Preview added support to three new CSS color features that are coming soon to other browsers. To test the features mentioned in this article you need to enable them on the Develop menu.

Let's review them and see how they apply to creating a color theme.


CSS Relative color syntax

Relative color allows us to manipulate and convert any color to any format. We can use it to create a color palette from any chosen color.

:root {
  --theme-primary: #8832CC;
}
Enter fullscreen mode Exit fullscreen mode

original color preview

We want to create a color palette based on our primary color. Each step should have the same hue and saturation as the original, but a different lightness value, going from lighter to darker.

We'll convert our color to the HSL format (hue, saturation, lightness) and modify the lightness parameter using the new Relative color syntax with the from keyword.

code preview: hsl(from var(--theme-primary) h s 30%)

color comparison: primary vs modified with 40% lightness

Not only we converted our primary color from Hex to HSL, we also made it darker by changing its lightness value to 30%.

Using this, we can now create our palette:

.bg-primary-100 {
  background-color: hsl(from var(--theme-primary) h s 90%);
}
.bg-primary-200 {
  background-color: hsl(from var(--theme-primary) h s 80%);
}
.bg-primary-300 {
  background-color: hsl(from var(--theme-primary) h s 70%);
}
...
Enter fullscreen mode Exit fullscreen mode

color palette based on original color, from lighter to darker

Because we're using CSS variables, if our --theme-primary color changes, all our .bg-primary-xxx classes will update automatically.

comparison of the same website with two different color themes


CSS Color-contrast

Our color palette and theme are looking great, and this might be just what we need. But sometimes that's not exactly what you want.

Say we want to allow the user to choose their background color, and have the rest of the colors to adapt to that background.

comparison of the same website with a light and a dark background

Here we're not looking to modify the original color, we need to modify our text and other elements to be light or dark depending on the chosen primary color. This is where the new color-contrast css function comes in handy.

.text-contrast-primary {
  color: color-contrast(var(--theme-primary) vs white, black);
}
Enter fullscreen mode Exit fullscreen mode

Our .text-contrast-primary class will automatically compare our --theme-primary color to the specified options white or black and choose the one with the best contrast.

Similarly we can add a class for a contrast background. To take things further let's also support Tailwind's bg-opacity classes so we can fade our background color.

.bg-contrast-primary {
  /* get which color we should use (white or black) */
  --contrast-color: color-contrast(var(--theme-primary) vs white, black);
  --tw-bg-opacity: 1;
  /* converting white or black to rgba to support alpha */
  background-color: rgba(from var(--contrast-color) r g b var(tw-bg-opacity));
}
Enter fullscreen mode Exit fullscreen mode

Our content section is done. The sidebar and navigation have the proper background color, but we can't use .text-contrast-primary on them since that's the same color as the background. We need the inverse of our contrast color.

preview design with contrast colors

So let's add this awkwardly long named class to fix that:

.text-contrast-primary-inverted {
  /* get contrast color */
  --contrast-color: color-contrast(var(--theme-primary) vs white, black);
  /* get the contrast color of that contrast color */
  color: color-contrast(var(--contrast-color) vs white, black);
}
Enter fullscreen mode Exit fullscreen mode

And here's our html:

<body class="bg-primary">
  <nav class="bg-contrast-primary bg-opacity-70 text-contrast-primary-inverted">Navigation</nav>
  <main class="text-contrast-primary">Content</main>
  <aside class="bg-contrast-primary bg-opacity-70 text-contrast-primary-inverted">Sidebar</aside>
</body>
Enter fullscreen mode Exit fullscreen mode

CSS color-mix

In our first example we made a color palette based on the Hue and Saturation of our primary color. If we apply that same code to this other color we get this:

primary color

color palette

Our original color doesn't appear in our palette. It falls somewhere between the second and third shade, not close to the middle. If we want our palette to be lighter and darker shades of our primary color we can use color-mix instead.

shades of primary color

.text-primary-lightest {
  color: color-mix(var(--theme-primary), white, 30%);
}
.text-primary-dark {
  color: color-mix(var(--theme-primary), black 10%);
}
.text-primary-darker {
  color: color-mix(var(--theme-primary), black 20%);
}
...
Enter fullscreen mode Exit fullscreen mode

You can mix any two colors this way, and specify different quantities, just like mixing real paint.

mixing primary color with red to make a soft error background color

Now we have a color for error messages that matches better with our primary color.

.text-error {
 color: color-mix(var(--theme-primary), #FF0000 50%)
}
Enter fullscreen mode Exit fullscreen mode

I hope you enjoyed this article. Comment which feature is your favorite and what other ways you can see yourself using them.

Top comments (7)

Collapse
 
d7460n profile image
D7460N • Edited

Thanks again for this article!

So much potential...

"Because we're using CSS variables, if our --theme-primary color changes, all our .bg-primary-xxx classes will update automatically."

Can this technique used to "automatically update" .bg-primary-xxx classes to minimum accessibility foreground/background contrast ratios (4.5:1)? Rather than a fixed black or white?

Collapse
 
fabiogiolito profile image
Fabio Giolito • Edited

Seeing the comment just now.
You can add any colors instead of white and black, you can even add more colors. The browser will pick the first one with good contrast. The spec for color-contrast is still in draft so things might change.
But for example you can do:

:root {
  --primary-color: #8832CC;
}
.notice {
  background: var(--primary-color);

  --lighter-color-version: hsl(from var(--theme-primary) h s 90%);
  --darker-color-version: hsl(from var(--theme-primary) h s 10%);

  color: color-contrast( var(--primary-color) vs var(--lighter-color-version), var(--darker-color-version));
}
Enter fullscreen mode Exit fullscreen mode

When changing primary color the background will be the primary color but text color will be a darker or lighter version of that same color automatically.

Collapse
 
primadev2 profile image
Primadev

Awesome.

Collapse
 
eziyadah profile image
Eiyad Z.

Great Article, thanks!

Collapse
 
bcowley1220 profile image
Brendan Cowley

Fantastic article! Great guide for color ignorant people like myself. Thank you!

Collapse
 
ahmedelhnony profile image
Ahmed Elhanony

I tried to use it with scss like
background-color: hsl(from var(--btn-color) h s 70%);

but I got an error sayes:
Error: wrong number of arguments (1 for 3) for `hsl'

Collapse
 
d7460n profile image
D7460N

This is really great. Thank you for sharing!