The cornerstone of the SASS preprocessor are loops. They allow any sequence of actions to be repeatedly executed. In SASS, loops often allow you to generate many classes, which can then be used in the project.
Before we get down to writing and understanding loops, we will introduce a basic example on which we will study all possible types of loops in the SASS preprocessor. Let's imagine that we have a list with 5 elements inside it: cat, dog, whale, bird, fish. For each element of the list we need to make a class of the following kind:
.icon-cat {
height: 32px;
width: 32px;
background: url("../icon/svg/cat.svg");
}
What paths can we take now? The first thing that comes to mind is the use of mixins. This is a really good option, which fully satisfies our needs:
$icons: "cat", "dog", "whale", "bird", "fish";
@mixin icon-32($icon-name) {
.icon-#{$icon-name} {
height: 32px;
width: 32px;
background: url("../icon/svg/#{$icon-name}.svg");
}
}
Now, to generate icons, we need to execute this myxin for each item in the list. That is, five times:
@include icon-32(nth($icons, 1));
@include icon-32(nth($icons, 2));
@include icon-32(nth($icons, 3));
@include icon-32(nth($icons, 4));
@include icon-32(nth($icons, 5));
The icons are done - you're gorgeous! You can sit back and relax. This is acceptable if we have five icons. But what if we have 20 icons in the project? Or even 100? Maybe you're creating your own library of icons. In that case, using the method above is not the best way. It will give you the results you need, but the number of lines you will have to copy is quite high. So, how can we make life easier? Well, we have loops in SASS for that. We'll try one of them right now!
The for
loop allows us to set the number of repetitions we need. This is very useful if we initially know, or can calculate, the required number of repetitions of a block of code.
To write such a loop, the @for
construction is used, for which you need to specify a counter. The counter is a simple mechanism in which a start value and an end value are specified. Each time you execute a code block, the loop first increments the counter by one and then checks to see if the value has exceeded the limit. If not, we execute the code block again.
The counter in the @for
loop can be written of one of two kinds. The examples below are made for a counter from 1 to 5:
from 1 to 5
. The counter with the to
keyword counts to a finite number, not including the finite number. In this variant the code block will be executed 4 times.from 1 through 5
. A counter with the through
keyword counts up to a finite number, including a finite number. A loop with this counter will execute the code block 5 times.All counter values are written to a variable whose name you specify immediately after the @for
keyword. The complete loop entry is as follows:
@for $i from 1 to 5 {
// A block of code that will be executed 4 times.
}
A variable declared as a counter can be used inside a loop. It is easy to see this in the following example:
@for $i from 1 to 5 {
$side: $i * 10;
.square-#{$side} {
display: block;
width: #{$side}px;
height: #{$side}px
}
}
The $i
counter was used to calculate the required side of the square. The result of the compilation is the following CSS code:
.square-10 {
display: block;
width: 10px;
height: 10px;
}
.square-20 {
display: block;
width: 20px;
height: 20px;
}
.square-30 {
display: block;
width: 30px;
height: 30px;
}
.square-40 {
display: block;
width: 40px;
height: 40px;
}
Note that we used to
in the counter condition, so 4 classes were generated.
Now the solution to the original problem becomes a bit easier. We can use the @for
loop and generate classes as many times as we need.
$icons: "cat", "dog", "whale", "bird", "fish";
@mixin icon-32($icon-name) {
.icon-#{$icon-name} {
height: 32px;
width: 32px;
background: url("../icon/svg/#{$icon-name}.svg");
}
}
@for $i from 1 through 5 {
@include icon-32(nth($icons, $i));
}
I think after this code you are great again and deserve to sit back. You may have noticed a problem with this code - you need to know the number of items in the list. And as long as this number is small, everything is fine again. But as soon as these items become 78, for example, it is not very pleasant to have to recalculate them manually every time to change the counter.
Maybe there is a way to give counting to SASS itself? Of course there is - you can use the length()
function and get the number of elements in the list. In that case, our code could look like this:
$icons: "cat", "dog", "whale", "bird", "fish";
$icons-length: length($icons);
@mixin icon-32($icon-name) {
.icon-#{$icon-name} {
height: 32px;
width: 32px;
background: url("../icon/svg/#{$icon-name}.svg");
}
}
@for $i from 1 through $icons-length {
@include icon-32(nth($icons, $i));
}
The second way to solve the problem of generating classes with icons is the @each
loop. This loop search through the values of a list or an associative array and execute a block of code for each value. With this loop, we don't need to know the size of the list or create counters. We work directly with each value.
The @each
loop is ideal for the task of generating classes based on a list or an associative array. In order to use it, you have to use the keyword @each
, after which you specify the variable in which the current value from the list will be stored. After that the list from which these values are taken is specified.
$list: 1, 2, 3;
@each $number in $list {
// code block
}
This loop can be read as follows: For each number in the list, execute and then a block of code. This is a nice way to bypass the list without having to create separate variables or refer to the list with nth()
as was done in the @for
loop variant.
Let's move our icon generation task by replacing the @for
loop with the @each
loop.
$icons: "cat", "dog", "whale", "bird", "fish";
@mixin icon-32($icon-name) {
.icon-#{$icon-name} {
height: 32px;
width: 32px;
background: url("../icon/svg/#{$icon-name}.svg");
}
}
@each $icon in $icons {
@include icon-32($icon);
}
Now this is really good. We used exactly the loop we needed, thereby reducing the number of lines and increasing readability.
Tip: Always pay attention to the operation you are doing. If you just need to execute a block of code a certain number of times, use the @for
loop. When working with lists/arrays and using their values, use the @each
loop. In most cases this separation will help make your code cleaner
In the example above, we saw how to bypass lists with @each
, but with associative arrays things are a bit different. As you may remember, unlike lists, which simply enumerate values, associative arrays have key-value pairs. The @each
loop allows you to get both key and value inside a code block. To do this, you have to specify a comma-separated variable for the key and a variable for the value. Otherwise, the principle is the same.
As an example, let's take the more complex structure of our list of icons, and in addition to the name as a key, they will also contain the size of the icons in the form of a list that we have in our project. In this variant, we will combine an associative array and a list.
$icons: (
"cat": (32, 64, 128),
"dog": (32, 64, 128),
"whale": (32, 64),
"bird": (16, 32, 64, 128),
"fish": (32, 64)
);
@mixin icon($icon-name, $icon-size) {
.icon-#{$icon-size}-#{$icon-name} {
height: #{$icon-size}px;
width: #{$icon-size}px;
background: url("../icon/svg/#{$icon-size}/#{$icon-name}.svg");
}
}
@each $icon, $sizes in $icons {
@each $size in $sizes {
@include icon($icon, $size);
}
}
After compiling, we get the following CSS code for the cat icons:
.icon-32-cat {
height: 32px;
width: 32px;
background: url("../icon/svg/32/cat.svg");
}
.icon-64-cat {
height: 64px;
width: 64px;
background: url("../icon/svg/64/cat.svg");
}
.icon-128-cat {
height: 128px;
width: 128px;
background: url("../icon/svg/128/cat.svg");
}
The rest of the classes will be generated similarly.
In this example, you can see that we can nest loops inside each other. These loops don't have to be of the same type. We can put @each
inside @for
and vice versa. In each of these cases, everything will work correctly. The important thing is that the variables from the outer loop also go into the inner loop. That's why inside the second @each
loop we could use the variable $icon
, which is created in the loop before it.
The last kind of loop that exists in SASS is the @while
loop. From its name, we can conclude that it works according to the principle while a condition is fulfilled.
For the @while
loop you must specify a condition. As long as the condition is met, the loop is executed. The same constructions as with the conditional @if/@else
constructions are used as a condition.
$count: 1;
@while $count < 5 {
.font-size-#{$count} {
font-size: #{$count}em;
}
$count: $count + 1;
}
After compiling, you will get the following CSS code:
.font-size-1 {
font-size: 1em;
}
.font-size-2 {
font-size: 2em;
}
.font-size-3 {
font-size: 3em;
}
.font-size-4 {
font-size: 4em;
}
The @while
loop is complicated by the fact that it is very easy to forget to work with a condition and go into an infinite loop. If you forget to specify $count: $count + 1;
, the loop will never end. Always make sure that the condition changes after each block of code is executed.
The Hexlet support team or other students will answer you.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.
Programming courses for beginners and experienced developers. Start training for free
Our graduates work in companies:
From a novice to a developer. Get a job or your money back!
Sign up or sign in
Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.