Dynamically generating colors with SASS for data visualization

Consider the following problem: you have a collection of elements with related values ranging from 0 to 120. You want to display a different color on a spectrum depending on the value. We ran into this problem while working on our internal time-tracking tool and found a pretty cool solution using SASS. In our case we had a collection of <div>s with classes ranging from .c-0 to .c-120. For values 0 to 100 we wanted to transition from gray to blue, for 101 to 110 transition from blue to purple, and from 110 to 120 transition from purple to red.

Screenshot 2015-12-04 09.42.49

SASS has a bunch of color functions specifically one that will be useful to us:

mix($color-1, $color-2, [$weight])

Mix allows you to mix two colors together and specify a weight percent of how much of each color should be mixed. So a weight of 100% will be all $color-1 and a weight of 0% will be all $color-2.

Combining this with a loop should allow us to output CSS classes that transition from one color to another. Let’s give it a try:

@for $i from 0 through 100 {
  .c-#{$i} {
    background: mix(blue, red, $i )

Since the index of the loop goes from 0 to 100 we can easily use it as the weight percentage for our color mixing. This will output 101 CSS classes from .c-0 to .c-100 each with a different color set as the background. The result is:

Screenshot 2015-12-04 14.44.28

Pretty cool right? Our requirements were a little different though; we needed the numbers to go up to 120 and include more than 2 colors. If we did multiple loops we could mix more colors and have a smooth transition by mixing the next color with the previous ending color.

$color-1: #eeeeee;
$color-2: #3498db;
$color-3: #8e44ad;
$color-4: #c0392b;

@for $i from 0 through 100 {
  .c-#{$i} {
    background: mix( $color-2, $color-1, 100 * $i )
@for $i from 101 through 110 {
  .c-#{$i} {
    background:mix( $color-3, $color-2, 100 * ($i - 101) / (110 - 101) )
@for $i from 111 through 120 {
  .c-#{$i} {
    background:mix( $color-4, $color-3, 100 * ($i - 111) / (120 - 111) )

Well that isn’t pretty, but it works. (This describes most of the code I write.) You’ll notice we need to do some extra calculations to get the proper weight. Since our ranges are now arbitrary $i isn’t always 0-100. So with a little math we can calculate it’s percentage within the range. The result:

Screenshot 2015-12-04 14.54.14

Great! But that last bit of code wasn’t so great. If we clean it up a bit it will be easier to make changes and variations.

$colors: #eeeeee, #3498db, #8e44ad, #c0392b;
$stops: 0, 100, 110, 120;

@for $c from 1 through length($colors)-1 {
  $s1: nth($stops, $c);
  $s2: nth($stops, $c+1);
  $c1: nth($colors, $c);
  $c2: nth($colors, $c+1);
  @for $i from $s1 through $s2 {
    .c-#{$i} {
      background: mix($c2, $c1, 100*($i - $s1)/($s2 - $s1) )

SASS lets you build lists and work with them in a way similar to arrays. So we can loop through all the colors we’ve defined and spit out all the CSS classes for transitioning from that color to the next. Now we can have as many colors as we want and place the stops anywhere just by editing the $colors and $stops lists.

Here are a few examples of what we can output.

$colors: red, orange, yellow, limegreen, cyan, blue;
$stops: 0, 24, 48, 72, 96, 120;

Screenshot 2015-12-04 15.04.22

$colors: #f9b6a5, #ef7890, #cb4d89, #883692, #57247d, #391752;
$stops: 0, 20, 40, 60, 80, 100;

Screenshot 2015-12-04 15.13.49

$colors: #9bd5cd, #33a7c9, #2164a0, #0e2973, #ff2400;
$stops: 0, 4, 16, 64, 68;

Screenshot 2015-12-04 15.20.36

We could improve this even further by making it a proper SASS mixin but we’ll leave that to another day. Hopefully this comes in handy for anyone looking to generate colors for data visualization.


Filed in: Web Development