Operators and Expression Evaluation
1. Arithmetic Operators (+, -, *, /, %)
| Operator | Description | Example | Result |
|---|---|---|---|
| + | Addition | 10px + 5px |
15px |
| - | Subtraction | 20px - 8px |
12px |
| * | Multiplication | 5px * 3 |
15px |
| / | Division (deprecated) | math.div(20px, 4) |
5px |
| % | Modulo (remainder) | 17px % 5px |
2px |
| + | Unary plus | +5px |
5px |
| - | Unary minus (negation) | -10px |
-10px |
Example: Arithmetic operations
@use 'sass:math';
// Basic arithmetic
.arithmetic {
// Addition
width: 100px + 50px; // 150px
margin: 1rem + 2rem; // 3rem
// Subtraction
height: 500px - 100px; // 400px
padding: 3em - 1em; // 2em
// Multiplication (one must be unitless)
font-size: 16px * 1.5; // 24px
line-height: 1.2 * 2; // 2.4
// Division - MUST use math.div() in Dart Sass
width: math.div(100%, 3); // 33.333%
height: math.div(200px, 2); // 100px
// Modulo
$remainder: 17px % 5px; // 2px
$mod: 23 % 7; // 2
}
// Unit handling
.units {
// Compatible units can be added
width: 100px + 2em; // ERROR: Incompatible units
// Same units
width: 100px + 50px; // 150px
// Multiplication requires one unitless
width: 10px * 2; // 20px (correct)
width: 10px * 2px; // ERROR: Can't multiply px * px
// Division
width: math.div(100px, 2); // 50px
width: math.div(100px, 2px); // 50 (unitless)
}
// Operator precedence
.precedence {
// Multiplication before addition
width: 10px + 5px * 2; // 20px (not 30px)
// Use parentheses for clarity
width: (10px + 5px) * 2; // 30px
// Division first
margin: 100px - math.div(50px, 2); // 75px
}
// Negative numbers
.negatives {
// Unary minus
margin: -10px;
top: -(20px + 5px); // -25px
// Subtraction vs negative
width: 100px - 20px; // 80px (subtraction)
width: 100px -20px; // 100px -20px (list!)
width: 100px (-20px); // 80px (subtraction with parens)
}
// Practical: Grid column calculation
@function grid-width($columns, $total: 12, $gutter: 30px) {
$column-width: math.div(100% - ($total - 1) * $gutter, $total);
@return $column-width * $columns + ($columns - 1) * $gutter;
}
.col-4 {
width: grid-width(4);
}
// Practical: Fluid spacing
@function fluid-space($min, $max, $min-vw: 320px, $max-vw: 1200px) {
$slope: math.div($max - $min, $max-vw - $min-vw);
$intercept: $min - $slope * $min-vw;
@return calc(#{$intercept} + #{$slope * 100}vw);
}
.container {
padding: fluid-space(16px, 48px);
}
// Practical: Modular scale
@function modular-scale($step, $base: 16px, $ratio: 1.5) {
@return $base * math.pow($ratio, $step);
}
h1 { font-size: modular-scale(3); } // 54px
h2 { font-size: modular-scale(2); } // 36px
h3 { font-size: modular-scale(1); } // 24px
Warning: The
/ operator is deprecated for division.
Always use math.div() in Dart Sass. Plain / is used for CSS font shorthand and
slash-separated values.
2. Comparison Operators (==, !=, <, >, <=, >=)
| Operator | Description | Example | Result |
|---|---|---|---|
| == | Equal to | 10px == 10px |
true |
| != | Not equal to | 10px != 20px |
true |
| < | Less than | 5 < 10 |
true |
| > | Greater than | 15 > 10 |
true |
| <= | Less than or equal | 10 <= 10 |
true |
| >= | Greater than or equal | 15 >= 10 |
true |
Example: Comparison operations
@use 'sass:color';
// Equality comparison
.equality {
// Numbers
$is-same: 10px == 10px; // true
$not-same: 10px == 20px; // false
// Strings
$str-eq: "hello" == "hello"; // true
$str-neq: "hello" != "world"; // true
// Colors
$color-eq: red == #ff0000; // true
$color-neq: red != blue; // true
// Lists
$list-eq: (1, 2, 3) == (1, 2, 3); // true
// null
$is-null: null == null; // true
}
// Numeric comparison
.numeric {
// Basic comparison
$greater: 20 > 10; // true
$less: 5 < 10; // true
$gte: 10 >= 10; // true
$lte: 10 <= 15; // true
// With units
$px-gt: 100px > 50px; // true
$rem-lt: 1rem < 2rem; // true
}
// Conditional styling based on comparison
@mixin responsive-font($size) {
font-size: $size;
@if $size > 24px {
line-height: 1.2;
} @else if $size > 16px {
line-height: 1.4;
} @else {
line-height: 1.6;
}
}
.title {
@include responsive-font(32px); // line-height: 1.2
}
// Practical: Color brightness check
@function is-light($color) {
@return color.lightness($color) > 50%;
}
@function contrast-color($bg) {
@if is-light($bg) {
@return #000;
} @else {
@return #fff;
}
}
.button {
background: #3498db;
color: contrast-color(#3498db); // #fff
}
// Practical: Breakpoint comparison
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
@mixin above($breakpoint) {
$value: map.get($breakpoints, $breakpoint);
@if $value {
@media (min-width: $value) {
@content;
}
}
}
.container {
padding: 1rem;
@include above(md) {
padding: 2rem;
}
}
// Practical: Range validation
@function clamp-value($value, $min, $max) {
@if $value < $min {
@return $min;
} @else if $value > $max {
@return $max;
}
@return $value;
}
.box {
width: clamp-value(150px, 100px, 200px); // 150px
height: clamp-value(50px, 100px, 200px); // 100px
padding: clamp-value(250px, 100px, 200px); // 200px
}
3. Boolean Operators (and, or, not)
| Operator | Description | Example | Result |
|---|---|---|---|
| and | Logical AND (both true) | true and true |
true |
| or | Logical OR (either true) | true or false |
true |
| not | Logical NOT (negation) | not false |
true |
Example: Boolean logic
@use 'sass:map';
@use 'sass:meta';
// Basic boolean operations
.boolean {
// AND - both must be true
$both: true and true; // true
$one: true and false; // false
// OR - at least one must be true
$either: true or false; // true
$neither: false or false; // false
// NOT - negation
$neg-true: not true; // false
$neg-false: not false; // true
}
// Complex conditions
@mixin validate($value, $min, $max) {
@if $value >= $min and $value <= $max {
// Valid range
width: $value;
} @else {
@error "Value must be between #{$min} and #{$max}";
}
}
.box {
@include validate(150px, 100px, 200px); // OK
}
// Multiple conditions with OR
@function is-valid-color($color) {
@return meta.type-of($color) == 'color' or $color == transparent;
}
.element {
@if is-valid-color(red) {
background: red;
}
}
// NOT with comparisons
@mixin hide-on-mobile($hide) {
@if not $hide {
@media (max-width: 768px) {
display: none;
}
}
}
// Truthiness in Sass
.truthiness {
// Truthy: everything except false and null
$truthy-vals: (
0, // truthy (unlike JavaScript!)
"", // truthy
(), // truthy
true
);
// Falsy: only false and null
$falsy-vals: (
false,
null
);
}
// Practical: Feature detection
$enable-animations: true;
$enable-shadows: true;
$enable-gradients: false;
@mixin button-style {
padding: 10px 20px;
@if $enable-animations and $enable-shadows {
// Both enabled
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
transition: all 0.3s;
} @else if $enable-animations or $enable-shadows {
// At least one enabled
@if $enable-animations {
transition: all 0.3s;
}
@if $enable-shadows {
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
}
@if not $enable-gradients {
background: solid-color;
}
}
// Practical: Permission checking
@function has-permission($user, $required-roles...) {
$user-roles: map.get($user, roles);
@each $role in $required-roles {
@if list.index($user-roles, $role) {
@return true;
}
}
@return false;
}
$admin: (roles: (admin, editor));
$is-admin: has-permission($admin, admin, super-admin); // true
// Practical: Validation with multiple checks
@function is-valid-size($size) {
$is-number: meta.type-of($size) == 'number';
$has-unit: not math.is-unitless($size);
$in-range: $size >= 0px and $size <= 1000px;
@return $is-number and $has-unit and $in-range;
}
.container {
@if is-valid-size(200px) {
width: 200px;
}
}
Note: In Sass, only
false and null are falsy. Unlike JavaScript,
0, "", and () are truthy.
4. String Concatenation and Interpolation
| Operation | Syntax | Description | Example |
|---|---|---|---|
| + operator | $str1 + $str2 |
Concatenate strings | "hello" + "world" → "helloworld" |
| Interpolation | #{$expression} |
Embed expression in string | "size: #{10px}" → "size: 10px" |
| Selector interpolation | .#{$class} |
Dynamic selector names | .#{$prefix}-button |
| Property interpolation | #{$prop}: value |
Dynamic property names | #{$side}-width: 1px |
| Quoted strings | "text" or 'text' |
Preserves quotes | Font family names |
| Unquoted strings | identifier |
No quotes in output | CSS values |
Example: String operations and interpolation
@use 'sass:string';
// String concatenation
.concat {
// With + operator
$full-name: "John" + " " + "Doe"; // "John Doe"
// Quoted + unquoted
$mixed: "font-" + family; // "font-family"
// Quote preservation (first operand)
$quoted: "hello" + world; // "helloworld"
$unquoted: hello + "world"; // helloworld
}
// Interpolation in strings
.interpolation {
$size: 16px;
$name: "title";
// In string
font: #{$size}/1.5 Arial; // 16px/1.5 Arial
content: "Size is #{$size}"; // "Size is 16px"
// Multiple interpolations
$msg: "#{$name} has size #{$size}"; // "title has size 16px"
}
// Selector interpolation
$prefix: "app";
.#{$prefix}-header { // .app-header
color: blue;
}
.#{$prefix}-footer { // .app-footer
color: gray;
}
// Dynamic BEM modifiers
@mixin bem-modifiers($block, $modifiers...) {
@each $modifier in $modifiers {
&--#{$modifier} {
@content;
}
}
}
.button {
@include bem-modifiers('button', primary, large, disabled) {
// Generates: .button--primary, .button--large, .button--disabled
display: inline-block;
}
}
// Property interpolation
@mixin border-style($side, $width, $color) {
border-#{$side}-width: $width;
border-#{$side}-color: $color;
border-#{$side}-style: solid;
}
.box {
@include border-style(top, 2px, red);
// border-top-width: 2px;
// border-top-color: red;
// border-top-style: solid;
}
// Practical: Dynamic margin/padding
@each $side in (top, right, bottom, left) {
@each $size, $value in (sm: 8px, md: 16px, lg: 24px) {
.m#{string.slice($side, 1, 1)}-#{$size} {
margin-#{$side}: $value;
}
.p#{string.slice($side, 1, 1)}-#{$size} {
padding-#{$side}: $value;
}
}
}
// Generates: .mt-sm, .mt-md, .mt-lg, .pt-sm, etc.
// Practical: Icon class generator
$icons: (home, user, settings, search);
@each $icon in $icons {
.icon-#{$icon} {
background-image: url("icons/#{$icon}.svg");
}
}
// Interpolation in calc()
.calc-demo {
$base: 100px;
$multiplier: 2;
// Need interpolation in calc
width: calc(#{$base} * #{$multiplier});
// With variables
$gap: 20px;
width: calc(100% - #{$gap * 2});
}
// Interpolation with operations
.operations {
$width: 100px;
$height: 50px;
// In custom properties
--size: #{$width + $height}; // --size: 150px
// In attribute selectors
[data-size="#{$width}"] {
width: $width;
}
}
// Quoted string handling
.quotes {
// Font families need quotes if spaces
$font: "Open Sans";
font-family: $font; // "Open Sans"
// Unquote if needed
font-family: string.unquote($font); // Open Sans
// Quote if needed
$unquoted: Arial;
font-family: string.quote($unquoted); // "Arial"
}
// Practical: Data URI builder
@function data-uri($mime, $data) {
@return url("data:#{$mime};base64,#{$data}");
}
.icon {
background: data-uri('image/svg+xml', 'PHN2Zy4uLjwvc3ZnPg==');
}
5. Color Operations and Manipulation
| Operation | Syntax | Description | Example |
|---|---|---|---|
| Addition | $c1 + $c2 |
Adds RGB channels | #010203 + #040506 → #050709 |
| Subtraction | $c1 - $c2 |
Subtracts RGB channels | #050709 - #040506 → #010203 |
| Multiplication | $color * $number |
Multiplies RGB channels | #010203 * 2 → #020406 |
| Division | math.div($c, $n) |
Divides RGB channels | div(#020406, 2) → #010203 |
| color.scale() | color.scale($c, $args) |
Scale properties fluidly | Recommended over +/- |
| color.adjust() | color.adjust($c, $args) |
Adjust by fixed amount | Recommended over +/- |
Example: Color arithmetic (legacy)
@use 'sass:color';
@use 'sass:math';
// DEPRECATED: Direct color arithmetic
.legacy-color-ops {
// Addition (adds each RGB channel)
$c1: #010203;
$c2: #040506;
$added: $c1 + $c2; // #050709
// Subtraction
$subtracted: $c2 - $c1; // #040303
// Multiplication (by number)
$doubled: #010203 * 2; // #020406
// Division
$halved: math.div(#020406, 2); // #010203
}
// MODERN: Use color module functions instead
.modern-color-ops {
$base: #6b717f;
// Lighten/darken (deprecated: lighten(), darken())
$lighter: color.scale($base, $lightness: 30%);
$darker: color.scale($base, $lightness: -30%);
// Adjust specific channels
$adjusted: color.adjust($base, $red: 20, $blue: -10);
// Saturate/desaturate
$saturated: color.scale($base, $saturation: 40%);
$desaturated: color.scale($base, $saturation: -40%);
// Transparency
$transparent: color.adjust($base, $alpha: -0.5);
}
// Practical: Color palette generation
$primary: #007bff;
$palette: (
primary: $primary,
primary-light: color.scale($primary, $lightness: 20%),
primary-lighter: color.scale($primary, $lightness: 40%),
primary-dark: color.scale($primary, $lightness: -20%),
primary-darker: color.scale($primary, $lightness: -40%),
primary-pale: color.mix(white, $primary, 80%),
primary-muted: color.scale($primary, $saturation: -60%)
);
// Practical: Tint and shade functions
@function tint($color, $percentage) {
@return color.mix(white, $color, $percentage);
}
@function shade($color, $percentage) {
@return color.mix(black, $color, $percentage);
}
.color-variants {
background: tint(#007bff, 20%); // 20% white
border-color: shade(#007bff, 20%); // 20% black
}
// Practical: Accessible contrast
@function contrast-ratio($c1, $c2) {
$l1: color.lightness($c1);
$l2: color.lightness($c2);
@if $l1 > $l2 {
@return math.div($l1, $l2);
}
@return math.div($l2, $l1);
}
@function accessible-color($bg, $light: white, $dark: black) {
$light-contrast: contrast-ratio($bg, $light);
$dark-contrast: contrast-ratio($bg, $dark);
@if $light-contrast > $dark-contrast {
@return $light;
}
@return $dark;
}
.button {
$bg: #3498db;
background: $bg;
color: accessible-color($bg);
}
// Practical: Color temperature adjustment
@function warm($color, $amount: 10%) {
@return color.adjust($color, $red: 20, $hue: -5deg);
}
@function cool($color, $amount: 10%) {
@return color.adjust($color, $blue: 20, $hue: 5deg);
}
// Channel extraction and manipulation
.channel-ops {
$color: #ff6b6b;
// Extract channels
$r: color.red($color); // 255
$g: color.green($color); // 107
$b: color.blue($color); // 107
// Modify individual channels
$more-red: color.change($color, $red: 200);
$more-green: color.adjust($color, $green: 50);
}
Warning: Direct color arithmetic with
+, -, * is deprecated. Use color.scale(), color.adjust(), or
color.change() from the sass:color module.
6. Unit Conversion and Calculation
| Operation | Description | Example | Notes |
|---|---|---|---|
| Compatible units | Same dimension can combine | 1in + 2.54cm → 2in |
Auto conversion |
| Incompatible units | Different dimensions error | 1px + 1em → ERROR |
Cannot add px and em |
| Unit multiplication | One must be unitless | 10px * 2 → 20px |
Creates compound units |
| Unit division | Can cancel units | div(100px, 2px) → 50 |
Unitless if same |
| math.unit() | Extract unit as string | unit(100px) → "px" |
Returns string |
| math.is-unitless() | Check if no unit | is-unitless(10) → true |
Boolean result |
| math.compatible() | Check if can combine | compatible(1px, 1in) → true |
Same dimension |
Example: Unit operations and conversions
@use 'sass:math';
// Compatible unit arithmetic
.compatible {
// Length units are compatible
width: 1in + 2.54cm; // 2in (cm converted)
height: 96px + 1in; // 192px (1in = 96px)
// Angle units
$angle: 180deg + 3.14159rad; // ~360deg
// Time units
$duration: 1s + 500ms; // 1.5s
}
// Incompatible units (errors)
.incompatible {
// width: 100px + 2em; // ERROR: Incompatible units
// height: 10px + 5%; // ERROR: Cannot combine
// Must use calc() for runtime calculation
width: calc(100px + 2em); // Valid CSS calc
height: calc(100% - 50px); // Valid CSS calc
}
// Multiplication and division
.math-with-units {
// Multiplication: one operand must be unitless
width: 10px * 2; // 20px (OK)
height: 5 * 3em; // 15em (OK)
// ERROR: 10px * 2px // Can't have px²
// Division
width: math.div(100px, 2); // 50px
height: math.div(100px, 2px); // 50 (unitless - units cancel)
// Percentage calculation
$width: math.div(300px, 900px) * 100%; // 33.333%
}
// Unit inspection
.unit-inspection {
$value: 16px;
// Get unit as string
$unit-str: math.unit($value); // "px"
// Check if unitless
$has-unit: math.is-unitless($value); // false
$no-unit: math.is-unitless(10); // true
// Check compatibility
$can-add-px-in: math.compatible(1px, 1in); // true (both length)
$can-add-px-em: math.compatible(1px, 1em); // false (different)
$can-add-deg-rad: math.compatible(1deg, 1rad); // true (both angle)
}
// Practical: Strip unit
@function strip-unit($number) {
@if math.is-unitless($number) {
@return $number;
}
@return math.div($number, $number * 0 + 1);
}
.strip {
$unitless: strip-unit(100px); // 100
$calc: $unitless * 2; // 200
}
// Practical: Convert px to rem
@function px-to-rem($px, $base: 16px) {
@return math.div($px, $base) * 1rem;
}
.convert {
font-size: px-to-rem(24px); // 1.5rem
margin: px-to-rem(32px); // 2rem
}
// Practical: Convert rem to px
@function rem-to-px($rem, $base: 16px) {
@return math.div($rem, 1rem) * $base;
}
// Practical: Percentage width calculator
@function percent-width($target, $context) {
@return math.div($target, $context) * 100%;
}
.column {
width: percent-width(300px, 1200px); // 25%
}
// Practical: Responsive unit converter
@function responsive-size($min, $max, $min-vw: 320px, $max-vw: 1200px) {
// Strip units for calculation
$min-val: strip-unit($min);
$max-val: strip-unit($max);
$min-vw-val: strip-unit($min-vw);
$max-vw-val: strip-unit($max-vw);
$slope: math.div($max-val - $min-val, $max-vw-val - $min-vw-val);
$intercept: $min-val - $slope * $min-vw-val;
@return calc(#{$intercept}px + #{$slope * 100}vw);
}
h1 {
font-size: responsive-size(24px, 48px);
}
// Unit conversion constants
$px-per-inch: 96px;
$px-per-cm: math.div($px-per-inch, 2.54);
$px-per-mm: math.div($px-per-cm, 10);
@function inches-to-px($inches) {
@return $inches * $px-per-inch;
}
// Compound units from multiplication
.compound {
// Creates compound units
$area: 10px * 20px; // 200px*px (rarely useful)
// Use division to create ratios
$ratio: math.div(16px, 1em); // Usually use unitless numbers
}
Operators Summary
- Arithmetic: Use
math.div()instead of/for division - Comparison: Works with numbers, strings, colors; returns boolean
- Boolean: Only
falseandnullare falsy (0 is truthy!) - Strings: Use
+for concatenation,#{}for interpolation - Colors: Prefer
color.scale()over direct arithmetic - Units: Compatible units convert automatically; use
calc()for incompatible - Operator precedence:
not→* / %→+ -→< > <= >=→== !=→and→or
Note: Sass operators evaluate at compile time, not runtime. Use
CSS
calc() for runtime calculations with viewport units or mixed incompatible units.