Farmer in a field of haystacks
Illustration by

Mixing Color for the Web with Sass

Color is one of the most powerful components of art and design. We use it to influence mood, create an environment, and tell a story. Over 125 years ago, a great impressionist painter changed the way we think about color by observing light’s role in it. So far, these observations have been largely lost on design for the web, but a preprocessor like Sass gives us a tool to shed new light on our color palettes.

Article Continues Below

One morning in 1890, Claude Monet began painting the haystacks outside his window. But he didn’t paint just one painting, and he didn’t even paint just one painting at a time. He would have his assistant cart out wheelbarrows of canvases and would work quickly and minimally on each one as the light changed throughout the morning. Sometimes he would work on a painting for just a few minutes before the lighting conditions had changed enough to warrant moving on to the next canvas. When he was finished, Monet had painted twenty-five canvases of the same haystacks in different sunlight, seasons, and weather. The same haystacks, the same base colors—yet presented in myriad ways.

Claude Monet, Haystacks: Snow Effect (1891). Scottish National Gallery; public domain image.
Claude Monet, Haystacks: Snow Effect (1891). Scottish National Gallery; public domain image.

Historically, our ability to translate this kind of flexibility to the web has been limited. We’ve neglected the art of mingling color for emotional impact, while making the most of statically declared CSS color codes. Meanwhile, manipulating color on the fly has been relegated to the arcane realm of programmers.

Thankfully, new tools give us more power over color than ever before. But although color on the web continues to march forward, CSS alone is still pretty inflexible. That’s where preprocessors become useful. Let’s explore some of the capabilities they can lend to our stylesheets:

  • Aliases help us better recognize which colors we’re using.
  • Lightening, darkening, and scaling give us fine-grained flexibility over palettes.
  • Color mixing unlocks our inner Monet and a whole new world of nuance and artistry.

A hex on hex codes#section2

Start with the color declaration: you have to know the exact values of your colors in order to use them. That means that, unless you’re using prefabricated named colors, your style sheet fills up with multiple instances of cryptic hex codes or ambiguous HSL numbers. CSS variables are on the horizon, and they’ll help clarify which color is which with natural language—but what if we don’t actually have a name for our color? That’s the kind of power CSS preprocessors give us. There are several out there to choose from, but my examples rely on Sass. Other preprocessors probably have similar functionality, but I’ll leave you to do that research on your own.

Let’s dig into this to see what I mean.

We’ll create a new brand and choose two colors to represent it. The first thing I’m gonna do is name the colors: $toolbox and $ol-blue.

Brand colors, $toolbox and $ol-blue, for a hypothetical site called Gullfoss Travel Supply Co.

Now that I’ve established my brand colors, I’ve used them to build a website for Gullfoss Travel Supply Co. The concept behind this hypothetical site is to revitalize well-designed luggage labels that show off where you’ve travelled around the world. Variations of my brand colors exist throughout this site in different (lighter) tints and (darker) shades.

Hypothetical site for Gullfoss Travel Supply Co.
Hypothetical site for Gullfoss Travel Supply Co.

Take, for example, this button:

An “Add To Cart” button using a simple gradient.

I wanted to give my button a sense of clickability, which I can easily achieve with a simple gradient. The button is based on the color I dubbed $toolbox. The highlight is a lighter version of the swatch and the shadow is a darker version.

Traditionally, I would write this in CSS like so:

	background-color: $toolbox;  // fallback
	background-image: gradient(
		hsl(0, 33%, 52%),  // highlight
		hsl(0, 41%, 39%);  // shadow

While the button color is based on one of my brand colors, two of these colors (my highlight and shadow) are not in my Sass constants. I had to figure them out on my own. I opened up a color picker and manually picked variations of the swatch. Not a big deal, really, but if I want to add a secondary button, this time based on $ol-blue, I’ll need to go back into the color picker once again and figure out the new values.

And each of these buttons needs a hover state, too! The hover highlights and shadows are going to be lighter than those on the normal button, so do I declare four more constants, or do I just fill these values in once and hope I don’t need to use them again later?

As it turns out, Sass can do this for me. It has built-in functions to process these colors without having to keep track of all the variations.

Packing up the color picker for Sass#section3

One way to lighten a color is to use the lighten function:

lighten($toolbox, 20%);

And to darken a color, we can use the darken function:

darken($ol-blue, 30%);

Simple as that! Now we have a pair of tools to mix color on the fly. Go wild! Okay, don’t go too wild. This can get a bit tricky. Consider this: if we lighten $toolbox by 50 percent, we get a very light version of $toolbox. But if we lighten $ol-blue by 50 percent, it becomes completely white. That’s because $ol-blue is a much lighter color than $toolbox.

In order to know how far we can lighten a color before it turns white, we have to know that color’s lightness value ahead of time. That information is conveniently encoded in its HSL notation. If we subtract the color’s lightness value from 100 percent, the result is the amount we can lighten a color to get to white.

x = 100% - l

Since $ol-blue’s lightness value is 60 percent, we can lighten it up to 40 percent before it becomes perfectly white. $toolbox’s lightness is 40 percent, so we can lighten it by 60 percent.

Table showing that when lightening our colors, $ol-blue turns white faster than $toolbox, because it has a higher base lightness value.
When lightening our colors, $ol-blue turns white faster than $toolbox, because it has a higher base lightness value.
Table showing that when darkening our colors, $toolbox turns black faster than $ol-blue, because it has a lower base lightness value.
When darkening our colors, $toolbox turns black faster than $ol-blue, because it has a lower base lightness value.

Therefore, in order to master this new color palette, we’ll simply need to memorize the lightness values of each of our colors. Kind of annoying, but hey, it’s better than memorizing hex codes, right? Sure! But I’ll do you one better.

Proportional palettes with color scaling#section4

Sass has another color function called scale-color() that can move a color’s components proportionally. scale-color() works on the red, green, and blue channels in RGB, and the saturation and lightness channels in HSL. (To adjust the hue similarly, you would use the aptly-named adjust-hue() function.)

As I noted before, if we were to lighten $ol-blue by 50 percent, it would become pure white, but if we were to scale the lightness with scale-color() by 50 percent—

scale-color($ol-blue, lightness, 50%);

—it would be halfway between the original color and white.

Now I know exactly how much to scale any of my colors to get to white: it’s always going to be 100 percent. If I scale $ol-blue’s lightness by 99 percent, it will still be 1 percent $ol-blue. Likewise for $toolbox or any other color you can dream up (barring colors that are already so light that they may round up to white earlier); they will always top out at 100 percent lightness.

You can more easily see what I mean with the following color table:

Table showing that when scaling the lightness of our colors, they become proportionally lighter, and therefore more predictable.
When scaling the lightness of our colors, they become proportionally lighter, and therefore more predictable.
Table showing that when scaling the darkness of our colors, they become proportionally darker, and therefore more predictable.
The darker variations are proportional, too.

With scale-color(), you can keep your color palette limited to your base constants, but still have incredible, intuitive flexibility with tints and shades. Now our gradient declaration might look something like this:

	background-color: $toolbox;  // fallback
	background-image: gradient(
		scale-color($toolbox, lightness: 50%),
		scale-color($toolbox, lightness: -30%);

button: hover,
button: focus{
	background-color: scale-color($toolbox, lightness: 50%);  // fallback
	background-image: gradient(
		scale-color($toolbox, lightness: 60%),
		scale-color($toolbox, lightness: -20%);

	background-color: $ol-blue;  // fallback
	background-image: gradient(
		scale-color($ol-blue, lightness: 50%),
		scale-color($ol-blue, lightness: -30%);

	background-color: scale-color($ol-blue, lightness: 50%),  // fallback
	background-image: gradient(
		scale-color($ol-blue, lightness: 60%),
		scale-color($ol-blue, lightness: -20%);

In this example, notice I’m only using two of my constants and scaling them as desired. In fact, this can be applied across the entire page. The content on the homepage of the Gullfoss Travel Supply Co. only uses two brand colors, scaled to different lightness values. Despite the simple palette, there’s still a lot of flexibility here.

Mastering color with mixing#section5

There’s one more way you can achieve these kinds of proportional palettes, and that’s with an even more intuitive, more powerful Sass function called mix().

If we want to tint $ol-blue by 60 percent, we’ll write:

mix(white, $ol-blue, 60%)

Think of it like mixing a tube of white paint into a tube of Ol’ Blue. Likewise, if we want to shade $toolbox, we’ll write:

mix(black, $toolbox, 30%)

It turns out that mixing with white and black does perceptually the same thing as scaling a color’s lightness but, conveniently, it’s shorter to type. Beyond that, mix can help you easily create a look and feel on your websites that was previously not possible. If we can mix colors like paint now, can we make our websites look more like paintings? I believe we can—but we have to think less like programmers and more like artists.

Consider, again, Monet’s haystack paintings. They’re a remarkable study of light, and wonderful from a purely aesthetic standpoint. But from a design standpoint, there’s a useful lesson to be found in them. In the words of another French impressionist, Pierre Bonnard, “Color does not add a pleasant quality to design—it reinforces it.” Remember the way the color of light affected the appearance of Monet’s haystacks. What if we could take our base colors and easily influence the color in our designs the way he did back in 1890?

Sass’s mix() function unlocks that for us. Let’s take our color palette again and add in just a couple extra colors: a highlight and a shadow. Now let’s mix our brand colors once more, but instead of simply mixing with black and white, let’s use our new colors:

A couple of new colors for our palette: $highlight and $shadow.

Suddenly the whole palette becomes warm and inviting, and the darker colors are rich and vibrant.

Color table showing that tinting with a yellow highlight gives the palette a sunnier appearance.
Tinting with a yellow highlight gives the palette a sunnier appearance.
Color table showing that shading with a complementary shadow makes the palette feel more natural.
Shading with a complementary shadow makes the palette feel more natural.

If I decide I don’t like this scheme, I can simply choose new values for those two constants, and the next time the Sass is compiled into CSS, the design will automatically reflect my change.

With this next scheme, I’m starting again with the same brand palette, but now the highlight is bright pink, while the shadow is a dark, desaturated green.

New $highlight and $shadow colors.

It totally changes the look of the palette, yet it remains based around our original brand.

Table showing that a change to the highlight and shadow colors is automatically reflected in your color palette when the Sass is compiled into CSS.
A change to the highlight and shadow colors is automatically reflected in your color palette when the Sass is compiled into CSS.
Table showing that highlights and shadows can be tweaked to achieve just the right mood or story for your site, without making tedious changes throughout your stylesheets.
Highlights and shadows can be tweaked to achieve just the right mood or story for your site, without making tedious changes throughout your stylesheets.

Looking back at Gullfoss Travel Supply Co., I’ve demonstrated some of the possibilities with this kind of color mixing on each of the sticker pages. Looking at Olympia’s page, the mood is totally different from the homepage, yet all of the markup, typography, and basic layout stay the same. But since nearly every color has been mixed to some degree with yellow highlights or purple shadows, a new light (literally) has been cast on the page. Now the content background is an eggshell color and the “Add to Cart” button is natural, yet vibrant.

The Olympia page of the Gullfoss Travel Supply Co.  site.

Lincoln’s sticker is colored strongly with tints and shades of red, so I wanted the page to reflect that. I chose reddish highlights and shadows to make the design cohere with the illustration.

The Lincoln page of the Gullfoss Travel Supply Co. site.

When you visit the page for Barton Springs Pool, the cool waters and green leaves are reflected throughout. The difference between the original colors and the new ones is subtle but distinct, and that’s the point. Your colors should work together to create an aesthetic that enhances your design.

The Barton Springs Pool page of the Gullfoss Travel Supply Co. site.

But if drama is what you’re after, look no further than The Grid. This page reverses highlights and shadows and lends a look inspired by the movie Tron. Quite a striking change achieved just by swapping out a few constants!

The Grid page of the Gullfoss Travel Supply Co. site.

Additional considerations for developing your palette#section6

Nearly every color on these pages is mixed with a highlight or shadow to one degree or another, but sometimes the elements in your design can look a little too homogenous, and they start to blend together. In such cases, feel free to supplement your designs with another set of color mixers. This can give the layers of your pages more depth and really make them pop.

Let’s look again at the page for Lincoln. Remember, I wanted to give it a reddish tint. It’s hard to read text against bright red, so I dialed the highlights back a lot; they’re barely red at all. Then I set the background to green. Because green is red’s complement, it plays a trick on your brain, making the very light colors appear redder, while still maintaining a pleasing contrast ratio. (Note: Because this site is responsive, the background layer isn’t visible on narrow screens.) These separate layers use very different highlights and shadows that interact with each other.

To pursue legibility and readability a bit further for a moment, it’s also essential to keep in mind the accessibility of your color schemes. Take another look at the page for The Grid. If you found it uncomfortable to read, you’re not alone! The menu at the top of the page suffers from a low contrast ratio. According to the WCAG guidelines, it should be 4.5:1, but it comes in well below at just 2.6:1! Good contrast ratios of text and background colors make using a site much more pleasant. There are plenty of tools and recommendations for exploring this topic further.

Before I conclude, I want to go over browser support real quick, just so it’s clear. Because all this color processing is compiled into basic CSS color declarations, everything gets translated into a static declaration, which, of course, every browser today can understand. This means that you can start playing around with these techniques today!

Color on the web has come a long way, and it continues to improve steadily as browsers and devices add support for new technologies. Meanwhile, preprocessor mixing has given color an evolutionary leap forward. It offers us unprecedented power to create tints and shades that help us tell our stories, give our palettes more nuance, and bring out our inner Monet.

About the Author

Justin McDowell

Justin McDowell leads the visual design and front-end development for the Evergreen State College in Olympia, Washington. Before that, he worked for the Arbor Day Foundation and founded Ignite Lincoln, where he directed four sold-out shows. His proudest accomplishment, however, is a space-opera theme song he wrote for his corgi.

8 Reader Comments

  1. Paul, a style guide is a good place to set limitations and expectations. One place to start might be standardizing the “color stops” of your swatches. For example, “only mix in multiples of 10%,” or “swatches should not deviate more than 25% from the original color”.

  2. Thank you Justin – this is a very useful way to think about color.

    When designing, we often use transparent layers of color on top of each other to come up with a starter palette of mixes, as well as their tints and shades. Doing the same thing in the browser feels like a logical next step, and would allow for better communication between designers and developers, when they don’t happen to be the same person.

  3. Great article, thanks!

    I’ve found myself using mix recently where I would have previously (and unnecessarily) using rgba or opacity (or colour picking from a psd). A much better approach especially if your mix colours are variables, eg

    background: mix($c-background, black, 40%);

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA