@while
The @while rule, written @while <expression> { ... }, evaluates its block if its expression returns true. Then, if its expression still returns true, it evaluates its block again. This continues until the expression finally returns false.
The .scss file:
@use "sass:math";
/// Divides `$value` by `$ratio` until it's below `$base`.
@function scale-below($value, $base, $ratio: 1.618) {
@while $value > $base {
$value: math.div($value, $ratio);
}
@return $value;
}
$normal-font-size: 16px;
sup {
font-size: scale-below(20px, 16px);
}and the .css file:
sup {
font-size: 12.36094px;
}How it works step by step
The function scale-below(20px, 16px) divides 20px by 1.618 repeatedly until the result is below 16px:
| Iteration | Value | $value > 16px? |
|---|---|---|
| Start | 20px | yes |
| 1 | 12.36094px | no, stop |
math.div(20px, 1.618) equals approximately 12.36px, which is below the 16px base, so the loop stops after one iteration and returns the value.
Generating rules with @while
@while can also generate CSS rules. A common pattern is building a z-index scale, where each level gets a fixed increment:
$z-index: 1;
@while $z-index <= 5 {
.layer-#{$z-index} {
z-index: $z-index * 10;
}
$z-index: $z-index + 1;
}.layer-1 {
z-index: 10;
}
.layer-2 {
z-index: 20;
}
.layer-3 {
z-index: 30;
}
.layer-4 {
z-index: 40;
}
.layer-5 {
z-index: 50;
}@while vs @for vs @each
| Directive | Best when | Iteration control |
|---|---|---|
@for |
You know the start and end values | Fixed numeric range |
@each |
You want to iterate over a list or map | Each item in a collection |
@while |
The stop condition is dynamic / computed | Custom, you manage the counter |
Avoiding infinite loops
Because @while doesn't manage its own counter, it's easy to accidentally write a loop that never ends. Two safeguards help:
- Always modify the loop variable inside the block so the condition will eventually be
false. - Add a safety cap, i.e., a counter that breaks the loop after a maximum number of iterations:
@function scale-below($value, $base, $ratio: 1.618) {
$iterations: 0;
@while $value > $base and $iterations < 100 {
$value: math.div($value, $ratio);
$iterations: $iterations + 1;
}
@return $value;
}Summary
@while <expression>repeats its block as long as the expression is truthy.- The expression is re-evaluated before each iteration; the loop stops when it returns
false. - You must update the loop variable yourself. Sass does not auto-increment.
- Prefer
@forfor known ranges and@eachfor lists/maps; reach for@whileonly when the stop condition is dynamic. - Add a safety cap to guard against accidental infinite loops.