Loops in CSS Preprocessors

Avatar of Miriam Suzanne
Miriam Suzanne on (Updated on )

If you’ve ever watched old sci-fi flicks, you know how powerful loops can be. Feed your robot nemesis an infinite loop, and kaboom. Robo dust.

Preprocessor loops will not cause dramatic explosions in space (I hope), but they are useful for writing DRY CSS. While everyone is talking about pattern libraries and modular design, most of the focus has been on CSS selectors. No matter what acronym drives your selectors (BEM, OOCSS, SMACSS, ETC), loops can help keep your patterns more readable and maintainable, baking them directly into your code.

We’ll take a look at what loops can do, and how to use them in the major CSS preprocessors: Sass, Less, and Stylus. Each language provides a unique syntax, but they all get the job done. There’s more than one way to loop a cat.

See the Pen
Walkcycle with music loop
by CSS-Tricks (@css-tricks)
on CodePen.

(animation by Rachel Nabors)

PostCSS is also popular, but it doesn’t provide any syntax of it’s own. While it’s sometimes called a post-processor, I’d call it a meta-preprocessor. PostCSS allows you to write and share your own preprocessor syntax. If you wanted, you could re-write Sass or Less inside PostCSS, but someone else beat you to it.

Loop Conditions

Star Trek wasn’t entirely wrong. If you’re not careful, infinite loops can slow down or crash your compiler. While that’s not a good way to vaporize evil robots, it will annoy anyone using your code. That’s why loops should always serve a limited purpose — usually defined by a number of incremental repetitions or a collection of objects.

In programming terms:

  1. While loops are generic, and will keep looping while any condition is met. Be careful! This is where infinite loops are most likely.
  2. For loops are incremental, running for a particular number of repetitions.
  3. For-Each loops iterate through a collection or list, considering each item one-at-a-time.

Each type of loop is more narrowly focussed than the previous. A for-each loop is just one type of for loop, which is one type of while loop. But most of your use-cases will fall into the more specific categories. I had trouble finding true while loops in the wild — most examples could have been handled better with for or for-each. That’s probably why Stylus only provides syntax for the latter. Sass provides unique syntax for all three, and Less doesn’t technically have looping syntax at all — but that won’t stop us! Let’s dive in.

Collection for-each Loops

Preprocessor loops are most useful when you have a collection (list or array) of items to loop over — like an array of social media icons and colors, or a list of state-modifiers (success, warning, error, etc). Because for-each loops are tied to a known collection of items, they tend to be the most concrete and understandable loops.

Let’s start by looping through a simple list of colors, to see how it works.

In Sass, we’ll use the @each directive (@each $item in $list) to get at the colors:

See the Pen Sass ForEach List by Miriam Suzanne (@mirisuzanne) on CodePen.

In Stylus, the for syntax (for item in list) handles collections:

See the Pen Stylus ForEach List by Miriam Suzanne (@mirisuzanne) on CodePen.

Less doesn’t provide a loop syntax, but we can fake it with recursion. Recursion is what happens when you call a function or mixin from inside itself. In Less, we can use mixins for recursion:

.recursion() {
  /* an infinite recursive loop! */
  .recursion();
}

Now we’ll add a when “guard” to the mixin, to keep it from looping infinitely.

.recursion() when (@conditions) { 
  /* a conditional recursive "while" loop! */
  .recursion();
}

We can make it a for loop by adding a counter (@i), which starts at 1, and increases with every repetition (@i + 1) as long as our condition (@i <= length(@list)) is met — where length(@list) restricts our loop-iterations to the same length as our collection. If we extract the next list item on each pass, we’ll have a hand-made for-each loop:

See the Pen Less ForEach List by Miriam Suzanne (@mirisuzanne) on CodePen.

In Less, you get to do everything the hard way. It builds character.

Social Media Buttons

Looping through lists can be useful, but more often you want to loop through objects. One common example is assigning different colors and icons to your social media buttons. For each item in the list, we’ll need the name of the site and the brand color for that social network:

$social: (
  'facebook': #3b5999,
  'twitter': #55acee,
  'linkedin': #0077B5,
  'google': #dd4b39,
);

Using Sass, we can access the key (network name) and value (brand color) of each pair using the syntax @each $key, $value in $array. Here’s the full loop:

See the Pen Sass Social Media Loop by Miriam Suzanne (@mirisuzanne) on CodePen.

Stylus has a similar syntax: for key, value in array

See the Pen Stylus Social Media Loop by Miriam Suzanne (@mirisuzanne) on CodePen.

In Less, we have to extract each side of the pair manually:

See the Pen LESS Social Media Loop by Miriam Suzanne (@mirisuzanne) on CodePen.

Incremental for Loops

For loops can run for any number of repetitions, not just the length of an object. You might use this to create a grid layout (for columns from 1 through 12), loop through the color wheel (for hue from 1 through 360), or number your divs with nth-child and generated content.

Let’s start with a loop through 36 div elements, providing a number and background-color on each, using :nth-child.

Sass provides a special for-loop syntax: @for $count from $start through $finish, where $start and $finish are both integers. If the starting value is larger, Sass will count down instead of up.

See the Pen Sass “for” loop by Miriam Suzanne (@mirisuzanne) on CodePen.

The through keyword means our loop will include the number 36. You can also use the to keyword, which does not include the final counter: @for $i from 1 to 36 would only loop 35 times.

Stylus has a similar syntax for incrementing, but to and through are replaced with ... and .. respectively:

See the Pen Stylus “for” loop by Miriam Suzanne (@mirisuzanne) on CodePen.

Stylus also provides a range() function, which allows you to change the size of one increment. Using for hue in range(0, 360, 10) would increase the count by 10 on each repetition.

Less will have to use recursive mixins again. We can create an argument for the number of iterations (@i), guard it with the condition when (@i > 0), and subtract one on every iteration — to make it act like a decreasing for-loop:

See the Pen Less “for” loop by Miriam Suzanne (@mirisuzanne) on CodePen.

It’s worth noting that CSS can also give us the nth-child-numbering without pre-processors. While CSS doesn’t have loop structures, it does provide a counter() that you can increment based on any number of DOM-related conditions, and use in generated content. Sadly it can’t be used outside the content property (yet), so our background-colors are not applied:

See the Pen CSS counter by Miriam Suzanne (@mirisuzanne) on CodePen.

Grid Systems

I use incremental loops occasionally in my abstract Sass toolkits, but almost never in actual stylesheets. The one common exception is generating numbered selectors, either with nth-child (like we did above) or in automatically-generated classes (often used for grid systems). Let’s build a simple fluid grid system without any gutters to make the math difficult:

See the Pen Sass For-Loop Grids by Miriam Suzanne (@mirisuzanne) on CodePen.

Each grid-span is a percentage, using the math span / context * 100% — the basic calculation all grid systems have to make. Here it is again in Stylus and Less:

See the Pen Stylus For-Loop Grids by Miriam Suzanne (@mirisuzanne) on CodePen.

See the Pen LESS For-Loop Grids by Miriam Suzanne (@mirisuzanne) on CodePen.

Unique Avatars

At OddBird, we recently designed an application with default user avatars — but we wanted the defaults to be as unique as possible. In the end, we designed only nine unique icons and used loops to transform them into 1296 different avatars, so most users would never see a duplicate.

Each avatar has five attributes:

<svg class="avatar" data-dark="1" data-light="2" data-reverse="true" data-rotation="3">
  <use xlink:href="#icon-avatar-1" xmlns:xlink="http://www.w3.org/1999/xlink"></use>
</svg>
  1. The starting icon shape (9 options)
  2. Rotatation of 0, 90, 180, or 270 degrees (4 options)
  3. A dark color for fill (6 options)
  4. A light color for background (6 options)
  5. A true/false attribute that reverses the colors (2 options)

The code has six colors, and three loops:

  1. @for $i from 0 through 3 gives us four rotations
  2. @for $i from 1 through length($colors) allows us to loop through the color list ($colors), and assign each color a number ($i). Normally I would use the @each loop to step through the collection of colors, but @for is simpler when I need a number for each item as well.
  3. The nested @each $reverse in (true, false) gives us the option to flip foreground and background for each color combination.

Here’s the final result in Sass:

See the Pen 1296 avatars using multiple loops by Miriam Suzanne (@mirisuzanne) on CodePen.

Converting that to Less and Stylus can be your homework. I’m already tired of looking at it.

Generic while Loops

True while loops are rare, but I do use them on occasion. I find them most useful when I’m following a path to see where it leads. I don’t want to loop through an entire collection or a specific number of iterations — I want to keep looping until I find what I’m looking for. This is something I use in my abstract toolkits, but not something you’ll need very often in day-to-day styling.

I built a toolkit to help me store and manipulate colors in Sass. Storing colors in variables might be the most common use-case for any pre-processor. Most people do something like this:

$pink: #E2127A;
$brand-primary: $pink;
$site-background: $brand-primary;

I know that pink is probably not the only color on your site, but it’s the only one e need for now. I gave it multiple names because it’s useful to establish layers of abstraction — from simple colors (pink), to broader patterns (brand-primary), and concrete use-cases (site-background). I also want to turn that list of individual colors into a palette that my pre-processor can understand. I need a way to say these values are all related, and part of a pattern. That way to do that, I store all my theme colors in a single Sass map, with key-value pairs:

$colors: (
  'pink': #E2127A,
  'brand-primary': 'pink',
  'site-background': 'brand-primary',
);

Why bother? I do it because I can point my style-guide generator at a single variable, and automatically create a color palette that stays up-to-date. But there are trade-offs, and it’s not the right solution for everyone. The map doesn’t allow me to make direct assignments across pairs, like I could with variables. I need a while loop to follow the breadcrumb trail of key-names, in order to find the value for each color:

See the Pen Sass “while” loop by Miriam Suzanne (@mirisuzanne) on CodePen.

I do that all the time, but if you search my code for Sass’s @while, you won’t find it. That’s because you can achieve the same thing with a recursive function, making it reusable:

See the Pen Sass “while” recursive function by Miriam Suzanne (@mirisuzanne) on CodePen.

Now we can call the color() function anywhere in our code.

Stylus has no syntax for while loops, but it does also allow array variables and recursive functions:

See the Pen Stylus “while” loop by Miriam Suzanne (@mirisuzanne) on CodePen.

Less doesn’t have array variables built-in, but we can mimic the same effect by creating a list of pairs, the same way we did for social-media colors:

@colors:
  'pink' #E2127A,
  'brand-primary' 'pink',
  'site-background' 'brand-primary'
;

We’ll have to create our own @array-get mixin to retrieve values from the array using key names, and then create our recursive while loop to follow the path:

See the Pen Less “while” list loop by Miriam Suzanne (@mirisuzanne) on CodePen.

That works for the purpose of demonstration, but there’s probably a better way to do this in Less, since you can alias and name-space variables without using an array (unlike Sass or Stylus):

See the Pen Less name-spaced variables by Miriam Suzanne (@mirisuzanne) on CodePen.

Now that my colors are successfully in one variable, I can use another loop to generate my color palette. Here’s a quick example in Sass:

See the Pen Sass color palette by Miriam Suzanne (@mirisuzanne) on CodePen.

I’m sure you could make that prettier than I did.

Getting Loopy!

If you’re not sure when to use loops in your code, keep an eye out for repetition. Do you have multiple selectors that follow a similar pattern, or a calculation you are doing over and over? Here’s how to tell which loop is best:

  1. If you can list and name the items in your loop, use for-each to cycle through them.
  2. If the number of repetitions is more important than any set of source items, or if you need your items numbered, use a for loop.
  3. If you’ll need to access the same loop with different inputs, try a recursive function instead.
  4. For anything else (almost never), use a while loop.
  5. If you’re using Less… Good luck!

Have fun looping!