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/@elsefor conditional logic and branching @foris best for numeric sequences with known ranges@eachis ideal for iterating over data (maps, lists)@whilefor 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.