Control Directives: @if, @else if, @else
Conditional Logic in SASS
SASS provides powerful control directives that allow you to write conditional logic in your stylesheets. The @if, @else if, and @else directives enable you to generate CSS conditionally based on variable values, perform different calculations, or create adaptive style systems. This makes your stylesheets more dynamic and intelligent.
@if Syntax
The @if directive evaluates a condition and includes a block of styles only if the condition is true. The syntax is straightforward and similar to conditional statements in other programming languages.
Basic @if Example
$theme: dark;
.header {
@if $theme == dark {
background-color: #333;
color: #fff;
}
}
// Compiled CSS:
// .header {
// background-color: #333;
// color: #fff;
// }
// If $theme was 'light', no styles would be generated for .header
// Using @if in property values
$enable-shadows: true;
.card {
padding: 20px;
border-radius: 8px;
@if $enable-shadows {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
}
// With shadows enabled:
// .card {
// padding: 20px;
// border-radius: 8px;
// box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
// }
@else if and @else
You can extend @if statements with @else if to test multiple conditions, and @else to provide a fallback when no conditions are met.
Complete Conditional Chain
$size: medium;
.button {
@if $size == small {
padding: 8px 16px;
font-size: 14px;
} @else if $size == medium {
padding: 12px 24px;
font-size: 16px;
} @else if $size == large {
padding: 16px 32px;
font-size: 18px;
} @else {
// Default fallback
padding: 10px 20px;
font-size: 15px;
}
}
// Compiled CSS (with $size: medium):
// .button {
// padding: 12px 24px;
// font-size: 16px;
// }
// Multiple conditions with different properties
$layout: grid;
$columns: 3;
.container {
display: flex;
@if $layout == grid {
display: grid;
@if $columns == 2 {
grid-template-columns: repeat(2, 1fr);
} @else if $columns == 3 {
grid-template-columns: repeat(3, 1fr);
} @else if $columns == 4 {
grid-template-columns: repeat(4, 1fr);
} @else {
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
} @else if $layout == flex {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
}
// Compiled CSS:
// .container {
// display: flex;
// display: grid;
// grid-template-columns: repeat(3, 1fr);
// }
Truthiness and Falsiness in SASS
Understanding what SASS considers "truthy" and "falsy" is crucial for writing correct conditional logic. In SASS, only two values are considered false: false and null. Everything else, including empty strings, zero, and empty lists, is considered true.
Truthy and Falsy Values
// FALSE values
$value1: false;
$value2: null;
// TRUE values (everything else!)
$value3: true;
$value4: 0; // Surprisingly TRUE!
$value5: ""; // Also TRUE!
$value6: (); // Empty list is TRUE!
$value7: #000; // Any color is TRUE
// Examples
.test-1 {
@if $value1 {
color: red; // Not included (false)
}
}
.test-2 {
@if $value2 {
color: blue; // Not included (null)
}
}
.test-3 {
@if $value4 {
color: green; // INCLUDED! (0 is truthy)
}
}
// Compiled CSS:
// .test-3 {
// color: green;
// }
// Checking for null vs false
$border-width: null;
.element {
@if $border-width {
border: $border-width solid #ccc;
} @else {
border: none;
}
}
// Result: border: none;
// Be careful with zero!
$margin: 0;
.container {
@if $margin {
margin: $margin; // This WILL be included!
}
}
// Result: margin: 0;
@if $value != 0 instead of @if $value.
Comparison Operators
SASS supports all standard comparison operators for building complex conditions.
All Comparison Operators
// == (equals)
$color: blue;
.equal-test {
@if $color == blue {
background: $color; // Included
}
}
// != (not equals)
$theme: light;
.not-equal-test {
@if $theme != dark {
background: white; // Included
}
}
// < (less than)
$width: 500px;
.less-than-test {
@if $width < 600px {
max-width: 100%; // Included
}
}
// > (greater than)
$font-size: 18px;
.greater-than-test {
@if $font-size > 16px {
line-height: 1.6; // Included
}
}
// <= (less than or equal)
$count: 5;
.less-equal-test {
@if $count <= 5 {
display: grid; // Included
}
}
// >= (greater than or equal)
$priority: 10;
.greater-equal-test {
@if $priority >= 10 {
z-index: 1000; // Included
}
}
// Comparing strings
$alignment: center;
.string-compare {
@if $alignment == center {
text-align: center; // Included
}
}
// Comparing colors
$bg: #fff;
.color-compare {
@if $bg == white {
color: black; // Included (color names resolve to hex)
}
}
Logical Operators
SASS provides logical operators to combine multiple conditions: and, or, and not.
AND Operator
// and - both conditions must be true
$is-premium: true;
$is-active: true;
.user-badge {
@if $is-premium and $is-active {
background: gold;
color: white;
}
}
// Compiled: background: gold; color: white;
// Multiple AND conditions
$screen-size: 1024px;
$orientation: landscape;
$high-res: true;
.responsive-layout {
@if $screen-size >= 1024px and $orientation == landscape and $high-res {
// All three conditions must be true
background-image: url('hero-large@2x.jpg');
}
}
OR Operator
// or - at least one condition must be true
$device: mobile;
.menu {
@if $device == mobile or $device == tablet {
display: none; // Hidden on mobile or tablet
}
}
// Chaining OR conditions
$role: editor;
.admin-panel {
@if $role == admin or $role == editor or $role == moderator {
display: block;
} @else {
display: none;
}
}
// Combining AND and OR (use parentheses for clarity)
$user-level: 5;
$is-verified: true;
$is-subscriber: false;
.premium-content {
@if ($user-level >= 5 and $is-verified) or $is-subscriber {
opacity: 1;
pointer-events: auto;
} @else {
opacity: 0.5;
pointer-events: none;
}
}
NOT Operator
// not - negates a condition
$dark-mode: false;
.content {
@if not $dark-mode {
background: white;
color: black;
}
}
// NOT with comparisons
$width: 300px;
.container {
@if not ($width > 500px) {
// Equivalent to: @if $width <= 500px
max-width: 100%;
}
}
// NOT with null check
$custom-font: null;
.text {
@if not $custom-font {
font-family: Arial, sans-serif; // Use default
} @else {
font-family: $custom-font;
}
}
// Complex logical expressions
$has-errors: true;
$is-loading: false;
$is-empty: false;
.form-state {
@if not $has-errors and not $is-loading and not $is-empty {
border-color: green; // Success state
}
}
@if ($a and $b) or ($c and $d)
Using @if in Mixins for Conditional Output
One of the most powerful applications of @if is creating mixins that conditionally generate different CSS based on parameters.
Responsive Mixin with Conditionals
// Breakpoint mixin with @if
@mixin respond-to($breakpoint) {
@if $breakpoint == phone {
@media (max-width: 599px) {
@content;
}
} @else if $breakpoint == tablet {
@media (min-width: 600px) and (max-width: 1023px) {
@content;
}
} @else if $breakpoint == desktop {
@media (min-width: 1024px) {
@content;
}
} @else if $breakpoint == large-desktop {
@media (min-width: 1440px) {
@content;
}
} @else {
@warn "Breakpoint '#{$breakpoint}' not recognized.";
}
}
// Usage
.sidebar {
width: 100%;
@include respond-to(tablet) {
width: 300px;
}
@include respond-to(desktop) {
width: 350px;
}
}
// Button variant mixin
@mixin button-variant($style: solid, $color: blue) {
@if $style == solid {
background-color: $color;
color: white;
border: 2px solid $color;
&:hover {
background-color: darken($color, 10%);
}
} @else if $style == outline {
background-color: transparent;
color: $color;
border: 2px solid $color;
&:hover {
background-color: $color;
color: white;
}
} @else if $style == ghost {
background-color: transparent;
color: $color;
border: none;
&:hover {
background-color: rgba($color, 0.1);
}
}
}
.btn-primary {
@include button-variant(solid, #007bff);
}
.btn-secondary {
@include button-variant(outline, #6c757d);
}
.btn-link {
@include button-variant(ghost, #007bff);
}
Spacing Mixin with Direction Control
@mixin spacing($size: md, $type: margin, $direction: all) {
$sizes: (
xs: 4px,
sm: 8px,
md: 16px,
lg: 24px,
xl: 32px
);
$value: map-get($sizes, $size);
@if not $value {
@error "Size '#{$size}' not found. Use: xs, sm, md, lg, xl";
}
@if $direction == all {
#{$type}: $value;
} @else if $direction == vertical {
#{$type}-top: $value;
#{$type}-bottom: $value;
} @else if $direction == horizontal {
#{$type}-left: $value;
#{$type}-right: $value;
} @else if $direction == top {
#{$type}-top: $value;
} @else if $direction == bottom {
#{$type}-bottom: $value;
} @else if $direction == left {
#{$type}-left: $value;
} @else if $direction == right {
#{$type}-right: $value;
} @else {
@warn "Direction '#{$direction}' not recognized.";
}
}
// Usage
.card {
@include spacing(md, padding, all);
// Result: padding: 16px;
}
.header {
@include spacing(lg, margin, vertical);
// Result: margin-top: 24px; margin-bottom: 24px;
}
.sidebar {
@include spacing(xl, padding, right);
// Result: padding-right: 32px;
}
Using @if in Functions for Conditional Return Values
Functions combined with @if allow you to return different values based on conditions, creating intelligent helper utilities.
Conditional Return in Functions
// Determine text color based on background lightness
@function text-color($bg-color, $threshold: 60%) {
@if lightness($bg-color) > $threshold {
@return #000; // Dark text for light backgrounds
} @else {
@return #fff; // Light text for dark backgrounds
}
}
.button-light {
$bg: #f0f0f0;
background-color: $bg;
color: text-color($bg);
// Result: color: #000;
}
.button-dark {
$bg: #2c3e50;
background-color: $bg;
color: text-color($bg);
// Result: color: #fff;
}
// Get value with fallback
@function get-theme-value($key, $fallback: null) {
$theme: (
primary: #007bff,
secondary: #6c757d,
success: #28a745
);
@if map-has-key($theme, $key) {
@return map-get($theme, $key);
} @else if $fallback {
@warn "Theme key '#{$key}' not found. Using fallback.";
@return $fallback;
} @else {
@error "Theme key '#{$key}' not found and no fallback provided.";
}
}
.element {
color: get-theme-value(primary);
// Result: color: #007bff;
}
.fallback-element {
color: get-theme-value(unknown, #999);
// Warning issued, Result: color: #999;
}
// Calculate responsive size
@function responsive-size($min, $max, $viewport-width) {
@if $viewport-width < 768px {
@return $min;
} @else if $viewport-width >= 768px and $viewport-width < 1200px {
// Linear interpolation between min and max
$progress: ($viewport-width - 768px) / (1200px - 768px);
@return $min + ($max - $min) * $progress;
} @else {
@return $max;
}
}
// Unit conversion with validation
@function to-rem($value) {
@if type-of($value) != number {
@error "Value must be a number. Got: #{type-of($value)}";
}
@if unitless($value) {
@warn "Value has no unit. Assuming pixels.";
$value: $value * 1px;
}
@if unit($value) == "rem" {
@return $value;
} @else if unit($value) == "px" {
@return ($value / 16px) * 1rem;
} @else {
@error "Cannot convert #{unit($value)} to rem.";
}
}
Real-World Examples
Theme Switching System
// Theme configuration
$current-theme: dark;
$themes: (
light: (
bg-primary: #ffffff,
bg-secondary: #f8f9fa,
text-primary: #212529,
text-secondary: #6c757d,
border-color: #dee2e6
),
dark: (
bg-primary: #212529,
bg-secondary: #343a40,
text-primary: #f8f9fa,
text-secondary: #adb5bd,
border-color: #495057
)
);
// Get theme value
@function theme($key) {
$theme-map: map-get($themes, $current-theme);
@if not $theme-map {
@error "Theme '#{$current-theme}' not found.";
}
@if map-has-key($theme-map, $key) {
@return map-get($theme-map, $key);
} @else {
@error "Key '#{$key}' not found in theme '#{$current-theme}'.";
}
}
// Apply theme
body {
background-color: theme(bg-primary);
color: theme(text-primary);
}
.card {
background-color: theme(bg-secondary);
border: 1px solid theme(border-color);
color: theme(text-primary);
}
.muted-text {
color: theme(text-secondary);
}
Responsive Helper with Breakpoint Detection
// Responsive typography mixin
@mixin responsive-font($min-size, $max-size, $min-vw: 320px, $max-vw: 1200px) {
font-size: $min-size;
@if $min-size != $max-size {
@media (min-width: $min-vw) {
font-size: calc(
#{$min-size} + #{strip-unit($max-size - $min-size)} *
((100vw - #{$min-vw}) / #{strip-unit($max-vw - $min-vw)})
);
}
@media (min-width: $max-vw) {
font-size: $max-size;
}
}
}
@function strip-unit($value) {
@return $value / ($value * 0 + 1);
}
// Usage
h1 {
@include responsive-font(24px, 48px);
}
p {
@include responsive-font(14px, 18px);
}
Direction-Aware Styles (RTL Support)
// Language direction
$direction: ltr; // Can be 'ltr' or 'rtl'
@mixin margin-start($value) {
@if $direction == rtl {
margin-right: $value;
} @else {
margin-left: $value;
}
}
@mixin margin-end($value) {
@if $direction == rtl {
margin-left: $value;
} @else {
margin-right: $value;
}
}
@mixin padding-start($value) {
@if $direction == rtl {
padding-right: $value;
} @else {
padding-left: $value;
}
}
@mixin padding-end($value) {
@if $direction == rtl {
padding-left: $value;
} @else {
padding-right: $value;
}
}
// Usage
.sidebar {
@include margin-end(20px);
// LTR: margin-right: 20px;
// RTL: margin-left: 20px;
}
.content {
@include padding-start(30px);
// LTR: padding-left: 30px;
// RTL: padding-right: 30px;
}
.icon {
@if $direction == rtl {
transform: scaleX(-1); // Flip icons in RTL
}
}
Exercise 1: Build a Button System
Create a comprehensive button system using @if conditionals:
- Create a
@mixin button($size, $variant, $state)that accepts three parameters - Size options: small, medium, large (different padding and font-size)
- Variant options: solid, outline, ghost (different background and border styles)
- State options: normal, disabled, loading (different opacity and cursor)
- Use nested @if statements to handle all combinations
- Generate 6 different button classes combining these options
Exercise 2: Theme Validation Function
Build a theme system with validation:
- Create a $themes map with at least 3 themes (light, dark, high-contrast)
- Each theme should have: background, text, primary, secondary, success, error colors
- Write a
@function theme-color($theme-name, $color-key)that returns the color - Use @if to validate that both the theme and color key exist
- Return appropriate @error messages for invalid inputs
- Add @warn for deprecated color names
- Generate CSS for cards, buttons, and alerts using your theme function
Exercise 3: Smart Spacing System
Create an intelligent spacing system:
- Write a
@function smart-space($multiplier, $context)function - Context options: compact, normal, spacious (base unit: 4px, 8px, 12px)
- Use @if to check if $multiplier is negative, zero, or positive
- Provide @warn for negative values
- Create a
@mixin apply-spacing($type, $direction, $multiplier, $context) - Type: margin or padding
- Direction: all, vertical, horizontal, top, right, bottom, left
- Generate 10 utility classes using this system