Using CSS color-mix To Create An Automatic Color Framework
I love abstracting reused styles into variables as much as the next developer, but when you get into colors, it can be a real headache.
I’ve found that translating just about any color framework into CSS variables just isn’t robust enough for any site that uses more than a few colors and shades.
Take a color framework like Tailwind‘s for example. I use that shading system for almost every website I build, and it’s great! Until I decide to change my neutral color from Slate to Zinc. Manually setting each shade’s hex code get’s old real quick.
:root {
--neutral-50: #f8fafc;
--neutral-100: #f1f5f9;
--neutral-200: #e2e8f0;
/* and so on... */
}
And that’s just the neutral colors! Don’t even get me started on brand colors, call-to-action colors, accents and so on. It’s almost easier to not use variables for colors in the first place and just use Find And Replace if I need to make a change!
I’m fed up, so I started researching ways to modify CSS colors. The first method I found was abstracting a base color out into it’s individual R, G, and B values, then inserting those values into an rgba() format with an opacity modifier to create shades. It looks like this:
:root {
/* using red as my base color */
--r: 255;
--g: 0;
--b: 0;
--base-100: rgba(var(--r), var(--g), var(--b), 10%);
--base-200: rgba(var(--r), var(--g), var(--b), 20%);
--base-300: rgba(var(--r), var(--g), var(--b), 30%);
/* and so on... */
}
Now if you decide to change the color, all you need to do is update the R, G, and B variables, and voila you have all your shades automatically. This is fine and all, but still clunky and limited:
Can’t create darker shades of your base color
Opacity can cause the color to blend in with the background. Work’s fine on white and black background, but nothing else.
Still clunky, I have to convert my HEX code to RGB, then update each value individually.
Surely there’s a better way, right?
Introducing color-mix
, newly available as of 2023. It’s functionality is as the name suggests, it mixes one color with another. The really great part of this, is that we can control how strongly we mix colors. Here’s a quick example showing how you can create light and dark shades of a base color by mixing white and black.
:root {
--base: #ff0000;
--base-light: color-mix(in srgb,var(--base),#fff 25%);
--base-dark: color-mix(in srgb,var(--base),#000 25%);
}
Pretty cool right? It’s pretty easy to start putting the pieces together on what’s possible. If you wan’t to get into the weeds of how color-mix
works, heres the documentation.
Fun tip: you can use any color format as your starting color, even CSS shorthands like “red” or “blue.”
Notice how I changed the variable naming schemed too? Who actually uses every shade? Instead I’ve opted for a simpler, more robust naming scheme. This way I can add extra in-between shades, or abstract out to different element types like a button’s hover state, so it does’t rely on a specific shade.
Here’s what I’ve come up with for a simple and robust CSS color framework.
:root {
--base: #ff0000;
--base-light: color-mix(in srgb,var(--base),#fff 25%);
--base-extra-light: color-mix(in srgb,var(--base),#fff 50%);
--base-dark: color-mix(in srgb,var(--base),#000 25%);
--base-extra-dark: color-mix(in srgb,var(--base),#000 50%);
--base-button-hover: color-mix(in srgb,var(--base),#000 10%);
}
Implementing this into something like Bricks Builder’s Global Variables is a breeze. You can simply copy and paste the code above into the import feature.
Let me know you’re thoughts, or if you have a better way of achieving this!