Grow your CSS skills. Land your dream job.

Create Striped Backgrounds With Gradients (and a Sass Mixin)

Published by Guest Author

The following is a guest post by Hugo 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%);
}
Check out this Pen!

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%
  );
}
Check out this Pen!

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. :)

Comments

  1. peter

    I’d rather use 50 spans with background-color than such a large amount of gradients.

    • lol ok go for it.

    • michelle

      I wonder which is actually more/less code, more/less efficiently rendered, and more/less browser compatible.

    • Daniele P

      @michelle probably a background-image is, imho, the best solution if you need the result of the last example

    • Camaron Ornelas

      The example opens up hundreds of possible projects you could use sass with, now to combine 3d CSS3 with this for some spectacular results. Great article.

    • @Daniele I agree. As cool as a lot of these pure CSS solutions are, in a lot of cases plain images are better for performance, implementation and maintainability.

      Great example of some advanced SASS though, if we could derive the mixed stop percentages computationally (instead of listing them out) then it would be far superior to the 50 spans solution.

    • I love this CSS gradient example above, and naturally if that wasn’t possible I think I’d fall back to an image. But Peter’s comment has be itching to brainstorm. What are some other crazy ways to do this?

      a list of styled LIs using nth-of-type
      typographically abused underscores (negative letterspacing, bold, possibly overflow:hidden within a larger container)
      one of those box-shadow tricks
      2,000,000 floated and coloured 1x1px divs
      Flash :P

      Sillyness aside: Fantastic read Hugo!

    • Permalink to comment#

      Once the function is written you’re ready to go, this would actually be way faster, and also this handles responsive and retina stuff beautifully, especially compared to background images.

    • Jason
      Permalink to comment#

      I actually agree. With Zen coding, you could do it in a few seconds.

    • Paul d'Aoust
      Permalink to comment#

      @Chris: After having listened to a few Shop Talk podcasts, I totally hear your voice when I read your comment. (Get out of my head!)

    • saikiran
      Permalink to comment#

      even me tooo….

  2. This is awesome, thanks for sharing it, Hugo!

    Now we just need to create a function on top of it that generates random color-stops based on the amount of colors we want, and it’s the best sass function ever created.

  3. Ana

    About using spaces as a separator. I’m horribly lazy, so I’ve done that a lot. Which is how I found out that a list like this $l: 5 -$a 7 doesn’t work out right – test (the second one, the one with the rotate transform).

  4. Paul d'Aoust

    Clever idea, using a mixin to make it easier to build stripes. (I definitely think a gradient or other sort of background image is way better than fifty <div>s as well.) Just a couple thoughts:

    You don’t need to use if(condition, false, true) to emulate ! — the ! operator in Sass is not. Therefore all you’d need is

    $auto: not (type-of($first-item) == list)
    

    Another way of doing it is

    $auto: length($first-item) == 1
    

    which tests to see if it is a list by its length. (Another weirdness to add to your list of list weirdnesses is that individual values can be treated as single-item lists in a lot of places, like the list functions.)
    The reason strings can be unquoted in Sass is that they can be unquoted in CSS — as long as they don’t have any weird characters in them.
    I think it’s handy to be able to refer to lists with either spaces or commas, because again, that’s how CSS does it. Tuples in CSS (e.g., a list of values for a background shorthand) are space-separated, and groups of them (e.g., multiple sets of values for the same shorthand) are comma-separated. In Sass you can nest lists as deeply as you like with sets of parentheses.

    • Hey, clever ideas to check whether $auto is true or false, I like that. Thanks a lot for the hint Paul. ;)

    • Paul d'Aoust
      Permalink to comment#

      No prob! I’ve been doing revolting amounts of Sass right now, doing things that you should never do in a preprocessor language, and I’ve picked up a lot of hints along the way.

  5. This is pretty awesome-thanks for sharing. Can’t follow everything yet because I’m still a beginner but I’m getting there. Also, do you need to be signed in to Codepen for those pens to work? Because I’m getting an image of a man with a magnifying glass saying ‘there’s nothing here’.

  6. My brain just exploded… I think i’m going to need to read through this again!

  7. Treehouse is using the spans due to compatibility issues. The entire thing isn’t working in Safari :(

  8. TeMc
    Permalink to comment#

    The examples don’t seem to work et all on Safari on latest iOS (6.1.4) on iPhone5. If it is indeed a matter if prefixes, could you fix the code pens? The article doesn’t have to mention them, but it makes it more attractive if the demos actually worked on a modern browser a developer is using to read this, else you’ll make me think this is a CSS feature I can’t even realistically use for a dev-audience.

    Thanks for the great article, nice to read from you again.

  9. bahaha @ “Go for it” Love it.

    *** Delete from here down after reading ***
    Chris (or moderator) on the blog home page “striped” is spelled “stripped” – don’t hate me for being such a word nerd. I love this site, don’t want anyone thinking ill of you ;)

    • Jimmy
      Permalink to comment#

      Since we are in grammar-nazi mode: later != latter
      And don’t get me started on how many times you have got “colour” wrong …

    • Paul d'Aoust
      Permalink to comment#

      @Jimmy: Since we’re in language-nazi mode, I feel compelled to point out that Chris didn’t get ‘colour’ wrong, as he’s an American and ‘color’ is acceptable spelling there :-) After all, let’s face it: English spelling is messed up anyway.

  10. This is pretty cool, however I can understand how this could create issues with older browsers. Don’t get me wrong this it a great little article but at the minute id just rather use “background-image… repeat…”.

    • True, but why couldn’t you use a fallback technique for older browsers? I think more developers need to embrace new technologies to properly move along HTML5/CSS3 and get it FULLY out of the door and into practise.

      If people never used new technologies, we couldn’t evolve.

    • Well, first of all, this would never cause issues in old browsers. At worst, the gradient simply isn’t display. Since this is clearly a graphical element, it is no big deal.

      Now as Richard said, this is progressive enhancement. Serve an image or even a flat border to old browsers, and this solution to modern ones. This can save a HTTP request to browsers which support gradients; it is a big deal.

  11. I’ve just started using CSS gradients (not so complex like these) on my site, but I had a hard time trying to make them look right in IE – I know, I know… LOL

    Still, a large part of my audience is still using IE and as such is a concern of mine.

    How compatible is everything you wrote in this article, cross-browser-wise and backwards version-wise, Hugo?..

    Cheers,

    ~Steve

    • Everything in this article is just syntax sugar thanks to Sass, so there is no kind of compatibility issue involved.

      However, we are dealing with gradients which are unfortunately not supported by Internet Explorer 9-. You can’t do much for this. Usually, we simply serve some fallbacks for older browsers like IE.

      In this case, I think I would use a solid border in any cases then place a gradient pseudo-element on top of it for supported browsers.

      Here is a quick and dirty proof of concept with comments: http://jsfiddle.net/kxjpk/

  12. I didn’t use the functions, but the first part of the article inspired me to make a css gradient version of the uk flag. I think it turned out quite nice. Tricky bit was handling the non-symetrical diagonal striping http://cdpn.io/GAKty

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".