Color blending in SCSS
Clean up your colors with custom blending functions in SCSS.
Kamil Nicieja
Unified Payroll is fully launched!
Read Story →Checklist: How to Evaluate a Potential Payroll Provider
Read Story →Pilot is now Plane!
Read Story →One of the conventions in Altimeter, our CSS components library, is how element states like hover and active are made. Instead of inventing new shades for each element, we have a system of overlaying and blending colors from our palette.For instance, a regular button in our CSS framework has the following states:Photo by the Pilot Team A naïve implementation would just copy the blended hex values from Sketch. However, almost exactly the same effect can be achieved by blending colors in Sass.While Sass provides a lot of useful functions for color handling, including the Photo by the Pilot Team The library did not support this use case: it would output an opaque color, even if two transparent colors where given.We’ve found that it’s not hard to implement blending calculations, so we’ve rolled out our own SCSS function for that purpose. Its usage is very similar:
mix
function for mixing colors, our needs were more complex—we needed Photoshop-like blending modes, as some of the more complex effects relied on modes like Difference. As it turns out, there’s a neat SCSS functions library that does exactly that.With the above library, our implementation closely reflects the specification presented above:.btn {
& {
border-color: $color-blue-350;
color: $color-blue-1000;
}
&:hover, &:focus {
border-color: blend-normal($color-dark-blue-a50, $color-blue-350);
color: blend-normal($color-dark-blue-a150, $color-blue-1000);
}
&:active {
border-color: blend-normal($color-dark-blue-a150, $color-blue-350);
color: blend-normal($color-dark-blue-a500, $color-blue-1000);
}
}
Although the naïve approach would have worked, we’ve benefitted significantly from the programmatic implementation. It’s much easier to deal with color variables and functions that clearly reflect the original intention than to deal with hex values copied from Sketch. It has also made it much easier to introduce variants of elements—for instance, when a green variant of the blue button was needed, it was only a matter of replacing the primary color, while carrying over the same calculations for hover and active states..btn--warning {
& {
border-color: $color-orange-350;
color: $color-orange-1000;
}
&:hover, &:focus {
border-color: blend-normal($color-dark-orange-a50, $color-orange-350);
color: blend-normal($color-dark-orange-a150, $color-orange-1000);
}
&:active {
border-color: blend-normal($color-dark-orange-a150, $color-orange-350);
color: blend-normal($color-dark-orange-a350, $color-orange-1000);
}
&.chevron {
@extend .chevron-m--negative;
}
}
Transparent output
The blending library served us well until a more advanced case occurred—we needed a button that would blend with the background in all states:.widget-button--outline {
& {
border-color: $color-blue-a350;
color: $color-blue-1000;
}
&:hover, &:focus {
border-color: blend-transparent($color-dark-blue-a150, $color-blue-a350);
}
&:active {
border-color: blend-transparent($color-dark-blue-a350, $color-blue-a350);
color: blend-normal($color-dark-blue-a350, $color-blue-1000);
}
}