Create a color theme with these upcoming CSS features
Tags: tutorial, webdev, css, tailwindcss
Published: 2021-04-05 21:30:13

[Safari Technology Preview](https://developer.apple.com/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.

``` css
:root {
  --theme-primary: #8832CC;
}
```

![original color preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1wy86cj13140ormiopu.png)

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%)](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dckau8tj0pd10zorvhu.png)
 
![color comparison: primary vs modified with 40% lightness](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oky2bd70o45kzfzk3760.png)
 
 
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:

``` css
.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%);
}
...
```
 
![color palette based on original color, from lighter to darker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2uu6i72arr0e4lrg5chu.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/md4x9dp477ls6bm3makb.png)

---

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sswccqaz95ce5k8yt4hx.png)
 
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.

``` css
.text-contrast-primary {
  color: color-contrast(var(--theme-primary) vs white, black);
}
```

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.

``` css
.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));
}
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ja0bc0zbmnuuuazp2ge.png)

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

``` css
.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);
}
```

And here's our html:

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

---

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddgy4jm8yzhq91domgdj.png)
 
![color palette](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0njdwp923z3dox9uqtsq.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vt817vd1nuhj0js7x7qq.png) 
 
``` css
.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%);
}
...
```
 
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/09avs7tgdjspalmf2hm1.png)

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

``` css
.text-error {
 color: color-mix(var(--theme-primary), #FF0000 50%)
}
```
 
---

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