Loop Through Sass Code Using Control Directives

Control directives give you a way to add conditional logic to your Sass code. They allow you to branch your code in different directions based on different conditions and they allow you to loop through code until certain conditions are true or they stop being true.

Public Address Booth Controls

Last week I started talking about control directives as part of a longer series about Sass data types, operators, and functions. I’ve now covered numbers, strings, colors twice, lists and list functions, and maps along with their associated operators and functions. Today I’ll finish up control directives and then we can move on to writing our own custom functions.

I’ve already covered the @if directive, the if() function and the @for directive. There are a couple more control directives to talk about, @each and @while.

The @each Directive

Like the @for directive we saw last week, the @each directive allows you to run code until certain conditions change. The difference is where a @for loop uses a counter, an @each loop loops the values in a list or map.

The @each directive is used to loop through the items in a list or map and continue to run the code you set until there are no more items left.

In the following code I created a list called $items and filled it with values of item-1, item-2 and so on up to item-10.

1
2
3
4
5
6
7
8
9
10
@items: (  
 item-1,  
 item-2,  
 …
 item-10  
);

@each $item in $items {  
 code to run here  
}

The @each directive says to look at the list $items and then for each value inside the list (or map) run the code. Each time through the loop, the next value inside the list (or map) becomes the variable $item and the code runs again. When there are no more items left, the program exists the block of code

For example you might have a set of buttons for your social profiles and each needs a different background image to distinguish it from the others. You could set up a list of sites and use the @each directive to create the proper class name and image name for the background-image.

1
2
3
4
5
6
7
8
9
10
11
$buttons: (  
 facebook,  
 twitter,  
 linkedin  
);

@each $button in $buttons {  
  .#{$button}-button {  
    background-image: url('/images/#{$button}}.png');  
  }
}

which compiles to:

1
2
3
4
5
6
7
8
9
10
11
.facebook-button {  
 background-image: url('/images/facebook.png');  
}

.twitter-button {  
 background-image: url('/images/twitter.png');  
}

.linkedin-button {  
 background-image: url('/images/linkedin');  
}

Adding more buttons would be a matter of adding the site name to the initial list, assuming of course you’ve created and named the images and placed them in the directory structure the loop expects.

Multiple Assignments

The @each directive isn’t limited to a single variable. You can loop through multiple values instead. Here’s a modified version of the previous example.

I created a nested list for the different buttons that includes the name of the social site and a color, which we’ll use to output the border color.

1
2
3
4
5
6
7
8
9
10
11
12
$buttons: (  
 (facebook red),  
 (twitter green),  
 (linkedin blue)  
);

@each $site, $color in $buttons {  
  .#{$site}-button {  
    background-image: url('/images/#{$site}}.png');  
    border: 1px solid $color;  
  }
}

Note the use of the two variables in the directive, $site for the name of each site and $color for the color that go with the site. Each time through the loop the value of $site is used to name the class and the name of the image, while $color is used to set the border color of the button.

The Sass compiles to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.facebook-button {  
 background-image: url('/images/facebook.png');  
 border: 1px solid red;  
}

.twitter-button {  
 background-image: url('/images/twitter.png');  
 border: 1px solid green;  
}

.linkedin-button {  
 background-image: url('/images/linkedin');  
 border: 1px solid blue;  
}

Multiple assignments are useful in maps as each map pair is like a nested list. I borrowed and modified this example from the Sass documentation. First I created a map named $headings, which contains key names of h1, h2, and h3 and setting each to a different font-size.

1
2
3
4
5
6
7
8
9
10
11
$headings (  
 h1: 3em,  
 h2: 2em,  
 h3: 1em  
);

@each $heading, $size in $headings {  
  #{$heading} {  
    font-size: $size;  
  }
}

Similar to the previous example the @each directive loops though both the key and value in each pair. It takes the value in each pair and sets it as the font-size of the key in the same pair.

The Sass compiles to:

1
2
3
4
5
6
7
8
9
10
11
h1 {  
 font-size: 3em;  
}

h2 {  
  font-size: 3em;  
}

h3 {  
  font-size: 1em;  
}

Look over this example a few times. It’s simple, but hopefully it shows how maps can be useful as well as how the @each directive with multiple assignments works.

The @while Directive

The @while directive is similar to the @for directive in that it sets a condition and the code inside the loop runs until the condition is no longer true. However, unlike the @for directive there’s no automatic counter. You have to set it inside the loop, which does allow you to do more complex things.

Here I set the variable $i to 10 and set a condition that the styles should be output while $i remains greater than 0. At the end of the loop I decreased the value of $i by 1 so the next time through the loop $i will equal 9 and so on until it’s been reduced to 0 and the styles inside the loop are no longer output.

1
2
3
4
5
6
7
$i: 10;

@while $i > 0 {  
  styles here;

  $i: $i - 1;  
}

If you remember last week I used a @for loop to create the widths for an eight column grid. Here’s the example rewritten to use a @while loop instead. I set two variables, one for $i which we’ll increment inside the loop and one for $cols, which represents the number of columns in our grid.

1
2
3
4
5
6
7
8
9
$i: 1;  
$cols: 8;

@while $i <= $cols  {  
 $width: percentage($i/$cols);  
 .col-#{$i} { width: $width; }

 $i: $i + 1;  
}

The condition is @while $i <= $cols so as long as $i is less than or equal to the value of $cols the code will continue to run.

The code that calculates the $width and sets a width for each column is the same as in the @for directive example from last week, but now we have to increment $i on our own so the last line in the loop adds 1 to the current value of $i.

The output is the same as it was in the @for example:

1
2
3
4
5
6
7
8
.col-1 { width: 12.5%; }  
.col-2 { width: 25%; }  
.col-3 { width: 37.5%; }  
.col-4 { width: 50%; }  
.col-5 { width: 62.5%; }  
.col-6 { width: 75%; }  
.col-7 { width: 87.5%; }  
.col-8 { width: 100%; }

You’ll find that @for and @while directives can often be rewritten as the other and which is best to use will depend on the specifics of what you’re trying to do.

Closing Thoughts

Control structures come in handy when you want to either branch in different directions based on certain conditions or to output similar styles with slight variations. The @if directive (or if() function) is useful for branching and the remaining directives can be used for the case of outputting slight variations.

The @for and @while directives loop through code as long as the condition you set remains true. The @each directive will loop through your code as long as there are still items in the list or map.

Again if you’ve done any programming, this has probably been a simple review of control structures. If you’re new to programming, I hope some of the examples here and in last week’s post give you an idea of what you can do with these control directives and how they can be useful.

Next week we’ll get to the last topic in this series. I’ll show you how to write your own custom functions.

Download a free sample from my book, Design Fundamentals.

2 comments

Leave a Reply

Your email address will not be published. Required fields are marked *