Create Striped Backgrounds With Gradients (and a Sass Mixin)

Avatar of Kitty Giraudel
Kitty Giraudel on (Updated on )

DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!

The following is a guest post by Kitty Giraudel. Hugo has written several times for CSS-Tricks including an article about a rather clever Pie Timer and several entries for the Almanac. I’m glad to have him back, this time explaining some pretty hardcore Sass stuff with a really practical application.

Hey guys! I’m glad to be able to write for CSS-Tricks again and share some cool tricks about Sass! In the past few weeks, I’ve been intensively experimenting with Sass lists. I have found a lot of things that likely most people don’t know.

I recently built a @mixin to create stripes. The idea is to give the mixin a regular gradient and it will turn it into a striped one. Since it turned out to be a fairly complicated thing to do, I thought it could be a good idea to write up.

Let’s begin with some reminders about how gradients work.

Striped Gradients

When you give a gradient two succesive color-stops with the same stop value, the transition between them two is abrupt instead of smooth. It is technically a gradient, but there is no smooth transition from one color to another. Consider the following code:

.el {
  /* This is smooth */
  background: linear-gradient(deepskyblue, tomato);

  /* This is striped */
  background: linear-gradient(deepskyblue 50%, tomato 50%);
}

You may put as many color-stops as you want as long as you remember to give each color-stop the same stop-value as the previous one (except for the first).

.french-flag {
  height: 10em;
  width: 15em;
  background: linear-gradient(
    to right, 
    blue 33.33%, white 33.33%, 
    white 66.66%, red 66.66%
  );
}

That’s pretty much everything you need to know about gradients for this article. If you ever happen to be interested in digging deep into the gradients, Ana Tudor wrote a wonderful article about this.

Sass Lists

Let’s take this from the very start. Lists in Sass are quite similar to arrays in JavaScript. To initialize a list: $list: ();.

Note you can also initialize it with $list: null; (or even $list: unquote("");) if you feel like it, but beware they are different. Anyway, here is a very simple Sass list:

$css-tricks-is: (#b4d455, 42, "awesome");

This gets a bit more complex, because the Sass syntax is very permissive when it comes to lists. You can do a ton of things, some of which you’re not supposed to. Let’s take them one by one.

You can ommit braces.

You are not forced to wrap your lists with braces and can totally ommit them if you feel like it. We often ommit them to be honest.

$css-tricks-is: #b4d455, 42, "awesome";

The first index is 1

Not 0 like arrays. This is unfortunate since it makes our upcoming example code more complicated. This is an important thing to know when you’re manipulating indexes with the nth() function.

$item-1: nth($css-tricks-is, 1); // #b4d455

You can separate values with spaces or commas.

Both are perfectly valid. I’m pretty sure you’ll prefer commas since it’s the standard separator for array values (JS, PHP…) but you can totally use spaces if you feel like it.

$css-tricks-is: #b4d455 42 "awesome";

You can mix spaces and commas.

You can have the first-level list comma-separated and the nested one space separated in nested lists. In the following example, the 3rd value is a space-separated list.

$css-tricks-is: #b4d455, 42, "awesome" "interesting" "free";

You can ommit quotes around strings.

That’s right, strings are not necessarily quoted in Sass. Don’t ask me why.

$css-tricks-is: #b4d455, 42, awesome;

In any case, I highly recommand you:

  • keep the braces
  • use commas
  • quote your strings

It will save you some time, trust me.

The Striping Mixin

Let’s get to the point of the article: our striping mixin! The main idea is to take advantage of the fact Sass lists can be comma-separated, just like color-stops in gradients. To put it simple, a list like $list: red 20%, blue 55% could be used in a linear-gradient() function as is: linear-gradient($list) outputing linear-gradient(red 20%, blue 55%).

Because we’re badasses, we want to make it as flexible as possible. So:

  • we can define a collection of color-stops
  • we can define a list of colors if we want all the stripes to have the same size
  • we define a direction as a default so we can ommit it in some cases

Thus we need two things to turn a regular gradient into a striped one: the list of colors / color-stops (e.g. deepskyblue, tomato, lightgreen or deepskyblue 20%, tomato 35%, lightgreen 62%) and an optional direction. Let’s start with the skeleton:

@mixin stripes($colors, $direction: "to bottom") { 
    /* Core */
}

As of $colors, there are 3 different scenarios available:

  • The user passes a comma-separated list of color-stops: (deepskyblue 20%, tomato 35%, lightgreen 62%),
  • The user passes a comma or space-separated list of colors: (deepskyblue, tomato, lightgreen),
  • The user passes a single color: deepskyblue or (deepskyblue).

Everything else will either dump out invalid output or throw a Sass error. The mixin is meant for informed developers in a development environment so it’s really no big deal if we don’t cover every single edge case.

The very first thing we have to find out is whether we are dealing with a list of colors or a list of color-stops. The easiest way to find out is to check the first item in the $color list (with type-of()). If it is a list, then we have to deal with color-stops else the user wants all stripes to have the same width and it’s pretty easy for us.

Unfortunately, we cannot do $auto : !(type-of($first-item) == list) since Sass doesn’t recognize this as valid syntax. So to check, we can use the if() Sass function which comes close to the var = condition ? true : false statement of other languages.

$auto: if( type-of(nth($colors, 1)) == list, false, true );

To sum up, if $auto is true, it means all stripes will have the same size so the calculations are handled by Sass. If it’s false, it means we have to deal with custom stops. We still need two variables before looping through colors: one in case we are running in auto-mode to define the size of a stripe; and one to store our color-stops while looping (later used in a linear-gradient function).

$stripe-width: 100% / length($colors); /* Only used in auto mode */
$gradient: ();

Now we can loop through colors / color-stops and add things to our $gradient variable. To add a numeric component to our loop, we use a @for loop instead of a @each loop. It doesn’t make things much harder though. We also declare 2 new variables inside the loop: one for the current item and one to store the current color-stop before appending it to $gradient.

@for $i from 1 through length($colors) {
    $item: nth($colors, $i);
    $dump: ();
}

This is where things get complicated. First, we have to differenciate auto mode from hard mode: color-stops are not the same. In auto mode, they are calculated which means in the first loop run (when $i equals 1) the “previous color-stop” equals 0% ($stripe-width * ($i - 1)). In hard mode, we have to check whether we’re in the first loop run or not because nth($colors, 0) isn’t allowed; it throws a Sass error. Let’s check the code:

@for $i from 1 through length($colors) {
    $item: nth($colors, $i);
    $dump: ();

    /* If we're in auto-mode,
     * $item equals a color,
     * color-stops are automatically calculated based on $i
     */
    @if $auto == true {
        $dump: $item $stripe-width * ($i - 1), $item $stripe-width * $i;
        /*      red   0%                      , red   50%
         * ^ This is what the first loop run would output with a 2 colors gradient
         */
    }
    
    /* If we're in hard-mode
     */
    @else {
        /* We check if it is the first run loop;
         * if it isn't, we add the current color with the previous stop
         */
        @if $i > 1 {
          $previous-item: nth($colors, $i - 1);
          $dump: append($dump, nth($item, 1) nth($previous-item, 2));
        }
        /* No matter what, 
         * we add the new color stop
         */
        $dump: append($dump, $item);   
   }

   /* And no matter what, 
    * we append $dump to $gradient using join(),
    * separating both with a comma by forcing it as a 3rd argument
    */
   $gradient: join($gradient, $dump, comma);
}

At the end of the loop, we have a well formated list ready to be used in a linear-gradient. We can now safely use it as background-image. Even with the Compass built-in mixin to output all the prefixes. Here is the whole mixin now:

@mixin stripes($colors, $direction: "to bottom") {  
    $stripe-width: 100% / length($colors);
    $auto:         if( type-of(nth($colors, 1)) == list, false, true );
    $gradient:     ();
    
    @for $i from 1 through length($colors) {
        $item: nth($colors, $i);
        $dump: ();
      
        @if $auto == true {
            $dump: $item $stripe-width * ($i - 1), $item $stripe-width * $i;
        }
      
        @else {   
            @if $i > 1 {
                $previous-item: nth($colors, $i - 1);
                $dump: append($dump, nth($item, 1) nth($previous-item, 2));
            }
            $dump: append($dump, $item);
        }

        $gradient: join($gradient, $dump, comma);
    }		

    @include background-image(linear-gradient($direction, $gradient));
}

Demo

As a demo, I took the header of Treehouse’s blog: a line made of about 50 colored stripes. It looks absolutely lovely. They currently use 50 spans. Ouch! They could use stripped gradients instead, and it’s easy now with this mixin.

I made two versions: the first one is running in auto-mode, meaning all stripes have the same width; the second one use custom color-stops to reproduce Treehouse’s effect.

/* Colors only (auto-mode) */
$treehouse-auto: #fa9300, #66c9ee, #c9c9c9, #82b964, #d24d33, #fffbdb, #2e77bb, #6bd5b1, #f87aa0, #c9c9c9, #72664e, #ccd600, #fffbdb, #df620e, #993838, #ff9600, #d24d33, #8960a7, #82b964, #f87aa0, #d43f3f, #668000, #ff9600, #8960a7, #c9c9c9, #993838, #ccd600, #668000, #f4cc13, #72664e, #fa9300, #66c9ee, #c9c9c9, #82b964, #ccd600, #fffbdb, #2e77bb, #6bd5b1, #f87aa0, #c9c9c9, #fa9300, #66c9ee, #c9c9c9, #82b964, #ccd600, #fffbdb, #fa9300;

/* Color-stops (hard-mode) */
$treehouse-hard: #fa9300 2.61%, #66c9ee 4.35%, #c9c9c9 6.96%, #82b964 9.13%, #d24d33 11.3%, #fffbdb 13.91%, #2e77bb 16.52%, #6bd5b1 17.82%, #f87aa0 19.12%, #c9c9c9 21.29%, #72664e 23.9%, #ccd600 26.07%, #fffbdb 28.68%, #df620e 31.29%, #993838 33.03%, #ff9600 34.33%, #d24d33 36.94%, #8960a7 39.55%, #82b964 42.16%, #f87aa0 43.36%, #d43f3f 45.63%, #668000 47.8%, #ff9600 50.41%, #8960a7 51.71%, #c9c9c9 53.88%, #993838 55.18%, #ccd600 57.79%, #668000 59.53%, #f4cc13 60.83%, #72664e 63.44%, #fa9300 66.05%, #66c9ee 67.35%, #c9c9c9 69.96%, #82b964 71.7%, #ccd600 74.31%, #fffbdb 76.92%, #2e77bb 79.53%, #6bd5b1 80.4%, #f87aa0 81.7%, #c9c9c9 83.87%, #fa9300 86.04%, #66c9ee 87.78%, #c9c9c9 90.39%, #82b964 92.56%, #ccd600 95.17%, #fffbdb 97.34%, #fa9300 100%;

/* Using a pseudo-element to display it, no extra markup */
.header:after {    
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    height: .5em;
    // Auto
    @include stripes($treehouse-auto, to right);
    // Hard
    @include stripes($treehouse-hard, to right);
}

As a result you can see the auto-mode at the top of the following pen and the hard-mode at the bottom. The later definitely looks better.

Check out this Pen!

Another use case could be helping out with the fluid width columns of equal height trick (see the Doug Neiner method).

Final words

I think we’re done team. If you prefer a function to a mixin, it is fairly easy to edit: simply change @mixin to @function and the background-image line to something like @return linear-gradient(#{$gradient}, #{$linear}) then use it with background-image: stripes($colors). I personally prefer using a mixin, but it’s really up to you.

What do you think?

If you feel like you could make the code easier, please be sure to tell! Thanks for reading. :)