Some of the most tedious work in writing CSS is also the most mechanical. A spacing scale with ten steps. A set of colour utility classes, one per brand colour. A grid system with twelve column widths. Writing those by hand means typing nearly the same rule over and over, and updating them later means editing every copy. SCSS loops turn that repetition into something you write once and let the compiler expand. This is the practical heart of keeping a stylesheet DRY: don’t repeat yourself when a loop can do the repeating for you.
What Loops Do Here
An SCSS loop iterates over something, a range of numbers, a list of items, or the entries in a map, and generates CSS rules on each pass. The value you are looping over feeds into the rules, usually through interpolation, so each iteration produces a slightly different class or property. The result is dozens of rules from a few lines of source.
There are three kinds of loop, and each fits a different shape of problem.
The @for Loop
The @for loop runs over a numeric range, which makes it the natural choice for anything based on a sequence of numbers like a spacing or sizing scale:
@for $step from 1 through 5 { .$step { margin: $step * 5px; }}
This generates five classes, .gap-1 through .gap-5, with margins of 5px, 10px, 15px, 20px, and 25px. The #{$step}interpolation injects the current number into the class name, and $step * 5px calculates the value.
One detail worth remembering is the difference between through and to. Using through includes the end value, so from 1 through 5 produces 1, 2, 3, 4, 5. Using to excludes it, so from 1 to 5 stops at 4. Mixing these up is a common source of off-by-one surprises, where you end up with one fewer class than you expected.
The @each Loop
When you are iterating over named things rather than numbers, @each is the right tool. It works over both lists and maps.
For a simple list, it walks through each item in turn:
$accent-colors: red, blue, green, orange;@each $color in $accent-colors { .$color { color: $color; }}
This produces .text-red, .text-blue, .text-green, and .text-orange, each setting the text colour to match its name. Both the class name and the property value come from the same loop variable.
Maps are where @each becomes especially useful, because you often want a human-friendly name in the class but a specific value in the property. A map lets you pair the two:
$theme-colors: ( primary: #3498db, secondary: #2ecc71, danger: #e74c3c);@each $name, $color in $theme-colors { .$name { background-color: $color; }}
Here the loop unpacks each entry into a name and a colour. The name becomes part of the class, so you get .bg-primary, .bg-secondary, and .bg-danger, while the hex value fills the background-color property. This is the standard pattern behind the colour utility classes you see in most design systems. Add a colour to the map and its class appears automatically.
The @while Loop
The @while loop repeats as long as a condition stays true. It produces the same kind of output as @for but gives you manual control over the counter:
$i: 1;@while $i <= 5 { .$i { padding: $i * 5px; } $i: $i + 1;}
The important line is $i: $i + 1. Without it, the condition $i <= 5 would never become false and the loop would run forever, hanging your compiler. This is the catch with @while: you are responsible for advancing the counter yourself, whereas @forhandles that automatically. For most numeric ranges, @for is simpler and safer. Reach for @while only when the stopping condition is genuinely something @for cannot express.
Keeping Loops Sensible
Guard against infinite loops. This mainly applies to @while, where forgetting to advance the counter, or writing a condition that never flips to false, will lock up compilation. Always confirm the loop has a clear path to ending.
Use interpolation to build dynamic selectors. The #{$variable} syntax is what lets a loop variable become part of a class name. Without it, you can compute values but not generate distinct selectors, which is usually the whole point.
Keep loops to repetitive, predictable work. Loops shine when the pattern is regular: a scale, a set of named colours, a series of widths. When the logic inside each iteration starts branching and growing complex, that is often a sign the work belongs in a mixin or function instead, with the loop simply calling it.
Comment loops that generate a lot of output. A loop that produces twenty utility classes is compact in the source but expansive in the compiled CSS. A short note explaining what it generates saves the next person from mentally compiling it to understand what classes exist.
The Takeaway
Loops are SCSS’s answer to mechanical repetition. @for handles numeric ranges, @each handles lists and maps, and @whilecovers the rarer cases where you need manual control over the stopping condition. The shared benefit is the same across all three: define the pattern once, and let the loop expand it into as many rules as you need. Add an item to a map or extend a range, and the corresponding CSS appears without you touching the rules themselves. That is what makes loops one of the most effective tools for keeping a large stylesheet both small in source and easy to maintain.
See you soon.
[…] Loops: https://datalad.co.uk/2024/04/11/generate-css-with-scss/ […]
[…] Loops: https://datalad.co.uk/generate-css-with-scss/ […]