Sass Reference
Article
By Hugo Giraudel

Loops

By Hugo Giraudel

Sass, like most programming languages, provides three different kinds of loops to iterate over collections (actually lists and maps):

  • the for-loop (using @for), to iterate from a number ‘A’ to a number ‘B’;
  • the each-loop (using @each), to iterate over a list or a map;
  • the while-loop (using @while), to iterate while a given condition is truthy.

While CSS is not a language that requires much logical conditions per se, it is not uncommon to have very repetitive declarative code in our stylesheets. To circumvent this, we can make good use of lists, maps and loops in order to make the code a bit more convenient to maintain.

For loop

The @for loop makes it possible to repeat a block of code n times, from a start index to an end index. The usual syntax for this loop is:

  1. the keyword @for
  2. the name of the index variable (e.g. $i)
  3. the keyword from
  4. the start index (e.g. 1)
  5. the keyword through to include the end index or to to exclude the end index
  6. the end index (e.g. 5)
@for $i from 1 through 5 {
    // 1
    // 2
    // 3
    // 4
    // 5
}

@for $i from 1 to 5 {
    // 1
    // 2
    // 3
    // 4
}

It is also possible to have decrementing @for loops, of course although some old Sass engines do not support them so be sure to refer to the Engine compatibility section for more information.

@for $i from -1 through -5 {
    // -1
    // -2
    // -3
    // -4
    // -5
}

@for $i from -1 to -5 {
    // -1
    // -2
    // -3
    // -4
}

Each loop

The @each loop is the ideal ally when having to loop over a list or a map. Indeed, this loop gives the ability to iterate over all items from a list or all pairs from a map using a variable to refer to the current item. The usual syntax for this loop is:

  1. the keyword @each
  2. the name of the current element variable (e.g. $item)
  3. the keyword in
  4. the list to iterate through
@each $item in $list {
    // Do something with `$item`
}

Note that the list does not necessarily have to be a variable. It is perfectly fine to inline a list directly in the loop, although this is something you might want to reconsider when looping through large lists.

@each $color in blue, white, red {
    // Do something with `$color`
}

When iterating over a map, the syntax is slightly different:

  1. the keyword @each
  2. the name of the current key (e.g. $key)
  3. a comma
  4. the name of the current value (e.g. $value)
  5. the keyword in
  6. the map to iterate through
@each $key, $value in $map {
    // Do something with `$key` and `$value`
}

While loop

While the @while loop might look like a weird tool, it actually makes perfect sense when you think about it. In everyday life, it is not uncommon to tell “do something until something else happens”, such as “wash it until it’s clean” for instance. This is exactly what the @while loop does. The usual syntax for this loop is:

  1. the keyword @while
  2. the condition
@while ($i % 2 == 0) {
    // Do something

    $i: fn($i);
}

Now you have to know that there is no way for Sass to break a @while loop like there is in other languages with a statement such as break;. The only way for Sass to get out of the loop is to have the condition not matching (basically false or null). Be sure to pay attention to your condition if you don’t want to enter an infinite loop!

Which loop when?

With three different ways of iterating other a collection, it is not always easy for inexperienced developers to pick the right tool for the right job. Fortunately, Sass being a CSS preprocessor (understand used to generate CSS) makes it actually quite easy to figure out which loop to use when.

If you have to know the iteration index, use a @for loop. For instance, when generating a series of :nth-child or :nth-of-type selectors, a @for loop is the best choice since all you need is an index. On the other hand, when looping over a list of a map, it is usually more convenient to opt for an @each loop rather than a @for loop. That’s it, @while loops are completely useless in Sass and should probably never be used.

Example

As an example, we will consider a list of 5 elements, with each one having its own color. If you don’t need specific colors, and just want to make a hue variation, a @for loop is a great option.

@for $i from 1 through 5 {
    li:nth-of-type(#{$i}) {
        color: hsl($i * 360 / 5, 75%, 75%);
    }
}

If you have a list of specific colors you want to apply to the list of items, you can keep a @for loop but you will probably have to change the code a bit:

$colors: red, orange, yellow, green, blue;

@for $i from 1 through length($colors) {
    li:nth-of-type(#{$i}) {
        color: nth($colors, $i);
    }
}

You could also rewrite the previous code with a @each loop but since you need to keep the index for the :nth-of-type selector, it means you will have to increment an index variable manually.

$colors: red, orange, yellow, green, blue;
$i: 1;

@each $color in $colors {
    li:nth-of-type(#{$i}) {
        color: $color;
    }

    $i: $i + 1;
}

Last but not least, if you want to apply a specific color to a specific class rather than based on an index, then you can move for good to a @each loop.

$colors: (
    'strawberry': red,
    'orange': orange,
    'lemon': yellow,
    'letuce': green,
    'blueberry': blue,
);

@each $section, $color in $colors {
    .#{$section} {
        color: $color;
    }
}

Engine compatibility

Loops are fully compatible across all Sass engines although Ruby Sass 3.2 had no support for decrementing @for loops. If you happen to be stuck with this old version of Sass and want to use a decrementing loop, there is a clever work around:

[code language='sass'] @for $i from -10 through -1 { $i: abs($i); } [/code]

This trick relies on the fact that the $i variable can only be incremented, so instead of going from 10 to 1, we go from -10 to -1, then get the absolute value of $i, the correct index.

  • The last code sample is missing a dollar sign for the color variable on line 9:
    @each $section, color in $colors

    • Thanks @kevinpelgrims:disqus, good catch. This is fixed now.

  • Паша Скул

    the gerat articl, thanks author