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>
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.

Discussion (4)

bcowley1220 profile image
Brendan Cowley

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

primadev2 profile image


eziyadah profile image
Eiyad Z.

Great Article, thanks!

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'