Control Flow and Conditional Logic

1. @if, @else if, @else Conditional Statements

Directive Syntax Description Use Case
@if @if condition { } Executes if condition is true Basic conditional logic
@else if @else if condition { } Alternative condition Multiple conditions
@else @else { } Fallback when all conditions false Default behavior
Truthy Values All except false and null Everything else evaluates to true 0, "", empty lists are truthy
Comparison Operators ==, !=, <, >, <=, >= Compare values Numeric/string comparisons
Logical Operators and, or, not Combine conditions Complex logic
Nested Conditions @if within @if Multi-level logic Complex decision trees

Example: Conditional statements in action

// Basic @if
$theme: dark;

.header {
  @if $theme == dark {
    background: #333;
    color: #fff;
  }
}

// @if / @else if / @else chain
@mixin button-size($size) {
  @if $size == small {
    padding: 4px 8px;
    font-size: 12px;
  } @else if $size == medium {
    padding: 8px 16px;
    font-size: 14px;
  } @else if $size == large {
    padding: 12px 24px;
    font-size: 16px;
  } @else {
    padding: 8px 16px;  // Default
    font-size: 14px;
  }
}

.btn-sm { @include button-size(small); }
.btn-lg { @include button-size(large); }

// Logical operators
$mobile: true;
$tablet: false;

.responsive {
  @if $mobile and not $tablet {
    width: 100%;
  }
}

// Comparison operators
@function get-font-weight($level) {
  @if $level > 700 {
    @return 900;
  } @else if $level > 500 {
    @return 700;
  } @else if $level > 300 {
    @return 400;
  } @else {
    @return 300;
  }
}

// Complex nested conditions
@mixin responsive-text($breakpoint, $emphasize: false) {
  @if $breakpoint == mobile {
    font-size: 14px;
    
    @if $emphasize {
      font-weight: 600;
      line-height: 1.4;
    }
  } @else if $breakpoint == tablet {
    font-size: 16px;
    
    @if $emphasize {
      font-weight: 700;
      line-height: 1.5;
    }
  }
}

// Truthy/Falsy checks
$config: null;

.component {
  @if $config {
    // This won't execute (null is falsy)
    margin: $config;
  } @else {
    margin: 1rem;  // Default
  }
}
Warning: In Sass, 0, "", and () are truthy. Only false and null are falsy.

2. @for Loop with from/through and to Syntax

Syntax Range Description Example
@for...through @for $i from 1 through 5 Inclusive end (1, 2, 3, 4, 5) Most common usage
@for...to @for $i from 1 to 5 Exclusive end (1, 2, 3, 4) Excludes last value
Loop Variable $i, $index, etc. Current iteration value Use in calculations
Ascending Start < End Counts up 1 through 10
Descending Start > End Counts down 10 through 1
Interpolation #{$i} Use variable in selectors/properties Dynamic class generation
Nested Loops @for within @for Multi-dimensional iteration Grid generation

Example: @for loop applications

// through vs to comparison
@for $i from 1 through 3 {
  .item-#{$i} { order: $i; }
}
// Generates: .item-1, .item-2, .item-3

@for $i from 1 to 3 {
  .col-#{$i} { width: $i * 10%; }
}
// Generates: .col-1, .col-2 (excludes 3)

// Utility classes generation
@for $i from 1 through 12 {
  .col-#{$i} {
    width: percentage($i / 12);
  }
}
// Generates: .col-1 through .col-12

// Spacing scale
$base-spacing: 4px;

@for $i from 1 through 10 {
  .mt-#{$i} { margin-top: $base-spacing * $i; }
  .mb-#{$i} { margin-bottom: $base-spacing * $i; }
  .ml-#{$i} { margin-left: $base-spacing * $i; }
  .mr-#{$i} { margin-right: $base-spacing * $i; }
}
// Generates: .mt-1 to .mt-10, etc.

// Z-index layers
@for $i from 1 through 5 {
  .layer-#{$i} {
    z-index: $i * 100;
  }
}

// Font size scale
@for $i from 1 through 6 {
  h#{$i} {
    font-size: (7 - $i) * 0.25rem + 1rem;
  }
}
// h1: 2.5rem, h2: 2.25rem, ..., h6: 1.25rem

// Descending loop
@for $i from 5 through 1 {
  .priority-#{$i} {
    opacity: $i * 0.2;
  }
}

// Nested loops for grid
@for $row from 1 through 3 {
  @for $col from 1 through 4 {
    .grid-#{$row}-#{$col} {
      grid-area: $row / $col;
    }
  }
}

// Animation delays
@for $i from 1 through 5 {
  .fade-in:nth-child(#{$i}) {
    animation-delay: #{$i * 0.1}s;
  }
}

3. @each Loop for Lists and Maps Iteration

Syntax Use Case Example Notes
List Iteration @each $item in $list Iterate over list values Single variable
Map Iteration @each $key, $value in $map Iterate over key-value pairs Two variables
Multiple Assignment @each $a, $b in $list Destructure list items For nested lists
Variable Scope Loop variable local to loop $item only exists in @each No leakage
Nested @each @each within @each Multi-level iteration Complex data structures
Interpolation #{$var} Use in selectors/properties Dynamic generation

Example: @each loop patterns

// Simple list iteration
$colors: red, green, blue, yellow;

@each $color in $colors {
  .text-#{$color} {
    color: $color;
  }
}
// Generates: .text-red, .text-green, etc.

// Map iteration (most common)
$theme-colors: (
  primary: #007bff,
  secondary: #6c757d,
  success: #28a745,
  danger: #dc3545,
  warning: #ffc107,
  info: #17a2b8
);

@each $name, $color in $theme-colors {
  .btn-#{$name} {
    background-color: $color;
    border-color: darken($color, 10%);
    
    &:hover {
      background-color: darken($color, 10%);
    }
  }
  
  .text-#{$name} { color: $color; }
  .bg-#{$name} { background-color: $color; }
}

// Multiple assignment (destructuring)
$icons: (
  ('home', '\f015'),
  ('user', '\f007'),
  ('search', '\f002'),
  ('settings', '\f013')
);

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

// Breakpoint generation
$breakpoints: (
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

@each $name, $width in $breakpoints {
  @media (min-width: $width) {
    .container {
      max-width: $width - 12px;
    }
  }
}

// Social media colors
$social: (
  facebook: #3b5998,
  twitter: #1da1f2,
  instagram: #e1306c,
  linkedin: #0077b5
);

@each $platform, $color in $social {
  .btn-#{$platform} {
    background: $color;
    color: white;
    
    &:hover {
      background: darken($color, 15%);
    }
  }
}

// Nested @each for comprehensive utilities
$spacing-sides: (t: top, r: right, b: bottom, l: left);
$spacing-values: (0: 0, 1: 4px, 2: 8px, 3: 16px, 4: 24px);

@each $side-key, $side-name in $spacing-sides {
  @each $size-key, $size-value in $spacing-values {
    .m#{$side-key}-#{$size-key} {
      margin-#{$side-name}: $size-value;
    }
    .p#{$side-key}-#{$size-key} {
      padding-#{$side-name}: $size-value;
    }
  }
}
// Generates: .mt-0, .mt-1, .pt-0, .pr-1, etc.

// Complex example: Button variants
$button-variants: (
  primary: (bg: #007bff, text: white, hover: #0056b3),
  secondary: (bg: #6c757d, text: white, hover: #545b62),
  outline: (bg: transparent, text: #007bff, hover: #007bff)
);

@each $variant, $props in $button-variants {
  .btn-#{$variant} {
    background: map-get($props, bg);
    color: map-get($props, text);
    
    &:hover {
      background: map-get($props, hover);
    }
  }
}

4. @while Loop for Complex Iterations

Feature Syntax Description Use Case
Basic Syntax @while condition { } Loops while condition true Unknown iteration count
Condition Check Before each iteration Exit when false Prevents infinite loops
Manual Increment Must update counter manually No automatic incrementing Custom step sizes
Infinite Loop Risk If condition never false Will hang compilation Always ensure exit condition
Use Over @for When logic is complex Dynamic termination Calculations, recursion alternatives
Less Common @for/@each preferred More predictable Use sparingly

Example: @while loop use cases

// Basic @while loop
$i: 1;

@while $i <= 5 {
  .item-#{$i} {
    width: 20% * $i;
  }
  $i: $i + 1;  // Manual increment required
}

// Fibonacci sequence generation
$fib-count: 10;
$fib-prev: 0;
$fib-curr: 1;
$i: 1;

@while $i <= $fib-count {
  .fib-#{$i} {
    width: #{$fib-curr}px;
  }
  
  $fib-next: $fib-prev + $fib-curr;
  $fib-prev: $fib-curr;
  $fib-curr: $fib-next;
  $i: $i + 1;
}

// Powers of 2 for grid columns
$col: 1;
$max: 16;

@while $col <= $max {
  .col-#{$col} {
    width: percentage($col / $max);
  }
  $col: $col * 2;  // 1, 2, 4, 8, 16
}

// Dynamic spacing based on golden ratio
$size: 1rem;
$ratio: 1.618;
$count: 1;

@while $size < 5rem {
  .spacing-#{$count} {
    margin: $size;
  }
  $size: $size * $ratio;
  $count: $count + 1;
}

// String processing (finding position)
@function find-and-mark($string, $target) {
  $i: 1;
  $result: ();
  
  @while $i <= str-length($string) {
    @if str-slice($string, $i, $i) == $target {
      $result: append($result, $i);
    }
    $i: $i + 1;
  }
  
  @return $result;
}

// Recursive-style calculation with @while
@function factorial($n) {
  $result: 1;
  $i: $n;
  
  @while $i > 1 {
    $result: $result * $i;
    $i: $i - 1;
  }
  
  @return $result;
}

.component {
  animation-duration: #{factorial(4)}ms;  // 24ms
}
Warning: Always ensure @while loops have a guaranteed exit condition. Infinite loops will freeze compilation.

5. Conditional Logic Best Practices and Performance

Best Practice Recommendation Reason Example
Prefer @each over @for Use @each for data iteration More readable, semantic Maps, lists of values
Guard Clauses Early return/exit Reduces nesting @if error, @return early
Limit Loop Iterations Keep under 100 iterations Compilation speed Large loops = slow builds
Avoid @while Use @for/@each when possible More predictable Known iteration counts
Cache Calculations Store in variables Avoid redundant computation Reuse map lookups
Specific Conditions Most specific @if first Early exit optimization Edge cases before general
Flat Conditionals Avoid deep nesting Readability Max 2-3 levels deep
Type Checking Validate before operations Prevent errors Check type-of first

Example: Best practices demonstration

// ❌ Bad: Deep nesting
@mixin button-bad($size, $variant, $disabled) {
  @if $size == large {
    @if $variant == primary {
      @if $disabled {
        // Too deep!
      }
    }
  }
}

// ✅ Good: Guard clauses and flat structure
@mixin button-good($size, $variant, $disabled) {
  // Guard clause - exit early
  @if $disabled {
    opacity: 0.5;
    pointer-events: none;
    @return;
  }
  
  // Flat conditions
  @if $size == large {
    padding: 12px 24px;
  }
  
  @if $variant == primary {
    background: blue;
  }
}

// ✅ Good: Cache repeated lookups
@mixin theme-styles($theme-name) {
  $theme: map-get($themes, $theme-name);  // Cache once
  
  @if $theme {
    background: map-get($theme, bg);
    color: map-get($theme, text);
  }
}

// ❌ Bad: Repeated lookups
@mixin theme-styles-bad($theme-name) {
  background: map-get(map-get($themes, $theme-name), bg);
  color: map-get(map-get($themes, $theme-name), text);
  // Looks up theme twice!
}

// ✅ Good: Specific conditions first
@function get-spacing($size) {
  // Edge cases first
  @if $size == 0 {
    @return 0;
  }
  
  @if $size < 0 {
    @warn "Negative spacing not allowed";
    @return 0;
  }
  
  // General case
  @return $size * 8px;
}

// ✅ Good: Type checking
@function safe-divide($a, $b) {
  @if type-of($a) != 'number' or type-of($b) != 'number' {
    @warn "Both arguments must be numbers";
    @return null;
  }
  
  @if $b == 0 {
    @warn "Cannot divide by zero";
    @return null;
  }
  
  @return $a / $b;
}

// ✅ Good: Prefer @each over @for for data
$sizes: (sm: 12px, md: 16px, lg: 20px);

// Preferred
@each $name, $size in $sizes {
  .text-#{$name} { font-size: $size; }
}

// Less preferred (but valid)
@for $i from 1 through length($sizes) {
  // More complex to access
}

6. Dynamic CSS Generation with Control Flow

Pattern Technique Output Use Case
Utility Classes @each over map Margin/padding utilities Atomic CSS frameworks
Theme Variants @each for color schemes Component color variants Design systems
Responsive Grids @for/@each for columns Grid column classes Layout systems
Animation Sequences @for with delays Staggered animations List animations
Breakpoint Utilities Nested @each Responsive utilities Mobile-first design
Color Scales @for with functions Tint/shade variations Palette generation
State Variations @if for states Hover/active/disabled Interactive components

Example: Complete utility generation system

// Comprehensive spacing utility generator
$spacing-scale: (
  0: 0,
  1: 0.25rem,
  2: 0.5rem,
  3: 0.75rem,
  4: 1rem,
  5: 1.5rem,
  6: 2rem,
  8: 3rem,
  10: 4rem
);

$spacing-properties: (
  m: margin,
  p: padding
);

$spacing-directions: (
  t: top,
  r: right,
  b: bottom,
  l: left,
  x: (left, right),
  y: (top, bottom)
);

@each $prop-abbr, $prop in $spacing-properties {
  @each $dir-abbr, $directions in $spacing-directions {
    @each $size-key, $size-value in $spacing-scale {
      .#{$prop-abbr}#{$dir-abbr}-#{$size-key} {
        @if type-of($directions) == 'list' {
          @each $dir in $directions {
            #{$prop}-#{$dir}: $size-value;
          }
        } @else {
          #{$prop}-#{$directions}: $size-value;
        }
      }
    }
  }
  
  // All sides
  @each $size-key, $size-value in $spacing-scale {
    .#{$prop-abbr}-#{$size-key} {
      #{$prop}: $size-value;
    }
  }
}
// Generates: .mt-0, .mt-1, .px-2, .m-4, etc.

// Responsive grid system with breakpoints
$grid-columns: 12;
$breakpoints: (
  sm: 576px,
  md: 768px,
  lg: 992px,
  xl: 1200px
);

// Base grid (mobile-first)
@for $i from 1 through $grid-columns {
  .col-#{$i} {
    width: percentage($i / $grid-columns);
  }
}

// Responsive variants
@each $bp-name, $bp-value in $breakpoints {
  @media (min-width: $bp-value) {
    @for $i from 1 through $grid-columns {
      .col-#{$bp-name}-#{$i} {
        width: percentage($i / $grid-columns);
      }
    }
  }
}
// Generates: .col-1 to .col-12, .col-md-1 to .col-md-12, etc.

// Color palette with tints and shades
$brand-colors: (
  primary: #3498db,
  secondary: #2ecc71,
  accent: #e74c3c
);

@each $name, $color in $brand-colors {
  .bg-#{$name} { background: $color; }
  .text-#{$name} { color: $color; }
  .border-#{$name} { border-color: $color; }
  
  // Generate tints (lighter)
  @for $i from 1 through 5 {
    $amount: $i * 10%;
    .bg-#{$name}-light-#{$i} {
      background: mix(white, $color, $amount);
    }
  }
  
  // Generate shades (darker)
  @for $i from 1 through 5 {
    $amount: $i * 10%;
    .bg-#{$name}-dark-#{$i} {
      background: mix(black, $color, $amount);
    }
  }
}

// Staggered animation system
@for $i from 1 through 10 {
  .fade-in-item:nth-child(#{$i}) {
    animation: fadeIn 0.5s ease-in;
    animation-delay: #{$i * 0.1}s;
    animation-fill-mode: both;
  }
}

// State-based component generator
@mixin generate-button-states($bg-color, $text-color) {
  background: $bg-color;
  color: $text-color;
  border: 1px solid darken($bg-color, 10%);
  
  &:hover {
    background: darken($bg-color, 8%);
  }
  
  &:active {
    background: darken($bg-color, 12%);
  }
  
  &:disabled {
    background: desaturate($bg-color, 50%);
    opacity: 0.6;
    cursor: not-allowed;
  }
  
  &:focus {
    outline: 2px solid $bg-color;
    outline-offset: 2px;
  }
}

$button-themes: (
  primary: (#007bff, white),
  success: (#28a745, white),
  danger: (#dc3545, white)
);

@each $variant, $colors in $button-themes {
  .btn-#{$variant} {
    @include generate-button-states(nth($colors, 1), nth($colors, 2));
  }
}

Control Flow Summary

  • Use @if/@else for conditional logic and branching
  • @for is best for numeric sequences with known ranges
  • @each is ideal for iterating over data (maps, lists)
  • @while for complex conditions, but use sparingly
  • Combine loops with functions for powerful generators
  • Keep iterations under 100 for optimal compilation performance
  • Use guard clauses and early returns to reduce nesting
  • Cache lookups and calculations to avoid redundant operations
Note: Control flow happens at compile time, not runtime. The generated CSS is static. For runtime logic, use CSS custom properties and JavaScript.