Episode 1 — Fundamentals / 1.7 — Working With SASS

1.7.h — Control Directives

In one sentence: SCSS control directives — @if, @for, @each, and @while — let you generate CSS programmatically, replacing hundreds of hand-written utility classes, grid columns, or color variants with a few lines of logic.

Navigation: ← 1.7.g — Functions & Operators · 1.7.i — Color Functions →


1. @if / @else if / @else

Basic conditional

@mixin theme($mode) {
  @if $mode == 'dark' {
    background: #1e293b;
    color: #f1f5f9;
  } @else if $mode == 'dim' {
    background: #334155;
    color: #e2e8f0;
  } @else {
    background: #ffffff;
    color: #1e293b;
  }
}

body { @include theme('dark'); }

Conditionals inside functions

@function text-contrast($bg) {
  @if lightness($bg) > 50% {
    @return #1e293b;
  } @else {
    @return #f8fafc;
  }
}

.banner {
  $bg: #3b82f6;
  background: $bg;
  color: text-contrast($bg);   // returns light text for dark blue
}

2. @for loop

Two flavors:

SyntaxRangeIteration
@for $i from 1 through 51, 2, 3, 4, 5Inclusive of end
@for $i from 1 to 51, 2, 3, 4Exclusive of end

Generate spacing utilities

@for $i from 1 through 8 {
  .mt-#{$i} {
    margin-top: $i * 0.25rem;
  }
}

Compiled CSS:

.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
/* … */
.mt-8 { margin-top: 2rem; }

Generate grid columns

@use 'sass:math';

$columns: 12;

@for $i from 1 through $columns {
  .col-#{$i} {
    width: math.div(100%, $columns) * $i;
  }
}

3. @each — iterating lists and maps

Iterate a list

$sizes: 'sm', 'md', 'lg', 'xl';

@each $size in $sizes {
  .text-#{$size} {
    font-size: var(--text-#{$size});
  }
}

Iterate a map

$colors: (
  'primary':   #3b82f6,
  'secondary': #64748b,
  'success':   #22c55e,
  'danger':    #ef4444,
  'warning':   #f59e0b,
);

@each $name, $color in $colors {
  .bg-#{$name} {
    background-color: $color;
  }
  .text-#{$name} {
    color: $color;
  }
}

Compiled CSS:

.bg-primary { background-color: #3b82f6; }
.text-primary { color: #3b82f6; }
.bg-secondary { background-color: #64748b; }
.text-secondary { color: #64748b; }
/* … and so on */

Destructuring lists of lists

$icons: (
  ('home',    '\e900'),
  ('search',  '\e901'),
  ('user',    '\e902'),
);

@each $name, $code in $icons {
  .icon-#{$name}::before {
    content: $code;
    font-family: 'Icons';
  }
}

4. @while loop

$i: 6;

@while $i > 0 {
  .width-#{$i} {
    width: $i * 100px;
  }
  $i: $i - 1;
}

@while is rarely used — @for and @each cover nearly all cases. Use @while only when the iteration count depends on a computed condition.

Danger: An infinite @while loop will hang the compiler. Always ensure the condition changes and will eventually become false.


5. Practical example — generating a full color palette

@use 'sass:color';

$base-colors: (
  'blue':   #3b82f6,
  'green':  #22c55e,
  'red':    #ef4444,
);

$shades: (100, 200, 300, 400, 500, 600, 700, 800, 900);

@each $name, $base in $base-colors {
  @each $shade in $shades {
    $lightness-adjust: ($shade - 500) * -0.1%;

    .color-#{$name}-#{$shade} {
      color: color.adjust($base, $lightness: $lightness-adjust);
    }
  }
}

This generates 27 classes (3 colors × 9 shades) from 10 lines of SCSS.


6. Practical example — responsive grid system

@use 'sass:math';

$breakpoints: ('sm': 640px, 'md': 768px, 'lg': 1024px);
$columns: 12;

@each $bp-name, $bp-value in $breakpoints {
  @media (min-width: $bp-value) {
    @for $i from 1 through $columns {
      .col-#{$bp-name}-#{$i} {
        width: math.div(100%, $columns) * $i;
      }
    }
  }
}

Output: .col-sm-1 through .col-sm-12, .col-md-1 through .col-md-12, .col-lg-1 through .col-lg-12 — 36 classes from ~10 lines.


7. Best practices

PracticeWhy
Prefer @each over @for when iterating named itemsMore readable — the loop variable has meaning
Avoid deeply nested loopsOutput CSS can explode in size — always check compiled output
Check compiled CSS sizeLoops can generate thousands of rules; only generate what you need
Use maps for configurationMaps + @each = data-driven CSS generation
Guard @while with a limitPrevent infinite loops during development

8. Key takeaways

  1. @if lets you conditionally emit CSS — commonly used in mixins and functions.
  2. @for generates numbered sequences — perfect for grids, spacing scales, and index-based classes.
  3. @each iterates lists and maps — the most versatile loop for named data.
  4. @while is a last resort — use only when the stop condition is computed dynamically.
  5. Control directives are compile-time — the browser receives flat CSS with no loops or conditions.

Explain-It Challenge

Explain without notes:

  1. What is the difference between @for $i from 1 through 5 and @for $i from 1 to 5?
  2. How would you generate .bg-primary, .bg-secondary, .bg-danger classes from a SCSS map?
  3. Why might generating too many classes with loops be a problem for production CSS?

Navigation: ← 1.7.g — Functions & Operators · 1.7.i — Color Functions →