Sass variables are simple: you assign a value to a name that begins with $, and then you can refer to that name instead of the value itself. But despite their simplicity, they're one of the most useful tools Sass brings to the table. Variables make it possible to reduce repetition, do complex math, configure libraries, and much more.
The importance of variables is that you can reuse the stored value throughout the stylesheet: change it once, and every reference updates.
Syntax
$variable-name: some value;A variable declaration looks a lot like a property declaration: it's written $variable: <expression>. Unlike a property, which can only be declared in a style rule or at-rule, variables can be declared anywhere you want. To use a variable, just include it in a value.
Example
The HTML file:
<!DOCTYPE html>
<html>
<head>
<title>Variables</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<body>
<div class="container">
<h1>Example using Variables</h1>
<p>Sass is an extension of CSS that adds power and elegance to the basic language.</p>
</div>
</body>
</html>The style.scss file:
$txtcolor: #008000;
$fontSize: 20px;
p {
color: $txtcolor;
font-size: $fontSize;
}Tell Sass to watch the file and update the CSS when it changes:
sass --watch style.scss:style.cssThe generated style.css file is as shown below:
p {
color: #008000;
font-size: 20px;
}A more practical example
A common pattern is to define a palette and spacing scale once at the top of the file:
$primary: #2196f3;
$secondary: #ff9800;
$success: #4caf50;
$danger: #f44336;
$base-font: 16px;
$line-height: 1.5;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
body {
font-size: $base-font;
line-height: $line-height;
}
.btn-primary {
background: $primary;
padding: $spacing-sm $spacing-md;
}
.btn-success {
background: $success;
padding: $spacing-sm $spacing-md;
}Change one variable and every rule that references it updates automatically.
Sass Variables vs CSS Variables
| Feature | Sass variables ($var) |
CSS variables (--var) |
|---|---|---|
| Compiled away? | Yes, gone from the final CSS | No, included in the CSS output |
| Multiple values? | One value at a time | Can differ per element |
| Update behavior | Imperative, earlier uses keep the old value | Declarative, changing the value affects all uses |
| Scope | Static (global / block) | Dynamic (DOM cascade) |
The imperative vs. declarative difference is the most important:
$variable: value 1;
.rule-1 { value: $variable; } // uses "value 1"
$variable: value 2;
.rule-2 { value: $variable; } // uses "value 2"Compiles to:
.rule-1 { value: value 1; }
.rule-2 { value: value 2; }Each use captures the value that was current at that point in the code. With CSS variables, a change would affect all uses.
Default Values (!default)
Normally when you assign a value to a variable, if that variable already had a value, its old value is overwritten. But if you're writing a Sass library, you might want to allow your users to configure your variables before you use them to generate CSS.
Sass provides the !default flag. This assigns a value to a variable only if that variable isn't defined or its value is null. Otherwise, the existing value is used:
// _library.scss
$black: #000 !default;
$border-radius: 0.25rem !default;
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15) !default;
code {
border-radius: $border-radius;
box-shadow: $box-shadow;
}// style.scss to override the defaults before use
@use 'library' with (
$black: #222,
$border-radius: 0.1rem
);Compiles to:
code {
border-radius: 0.1rem;
box-shadow: 0 0.5rem 1rem rgba(34, 34, 34, 0.15);
}The user's overrides (#222 and 0.1rem) take precedence; $box-shadow keeps its default since it wasn't overridden.
Scope
Variables declared at the top level of a stylesheet are global: they can be accessed anywhere in their module after they've been declared. Those declared in blocks (curly braces in SCSS or indented code in Sass) are usually local, and can only be accessed within the block they were declared.
$global-variable: global value;
.content {
$local-variable: local value;
global: $global-variable;
local: $local-variable;
}
.sidebar {
global: $global-variable;
// This would fail, because $local-variable isn't in scope:
// local: $local-variable;
}Compiles to:
.content {
global: global value;
local: local value;
}
.sidebar {
global: global value;
}Shadowing
Local variables can even be declared with the same name as a global variable. If this happens, there are two different variables with the same name: one local and one global. This ensures that writing a local variable doesn't accidentally change the value of a global variable you aren't aware of.
$variable: global value;
.content {
$variable: local value; // shadows the global $variable
value: $variable;
}
.sidebar {
value: $variable; // still the global value
}Compiles to:
.content { value: local value; }
.sidebar { value: global value; }The !global flag
If you need to set a global variable's value from within a local scope (such as in a mixin), use the !global flag. A variable declaration flagged as !global will always assign to the global scope:
$variable: first global value;
.content {
$variable: second global value !global;
value: $variable;
}
.sidebar {
value: $variable;
}Compiles to:
.content { value: second global value; }
.sidebar { value: second global value; }Flow control scope
Variables declared in flow control rules (@if, @each, @for, @while) have special scoping rules: they don't shadow variables at the same level as the flow control rule: they just assign to them. This makes it easy to conditionally assign a value to a variable:
$dark-theme: true !default;
$primary-color: #f8bbd0 !default;
$accent-color: #6a1b9a !default;
@if $dark-theme {
$primary-color: darken($primary-color, 60%);
$accent-color: lighten($accent-color, 60%);
}
.button {
background-color: $primary-color;
border: 1px solid $accent-color;
border-radius: 3px;
}The assignments inside @if modify the outer $primary-color and $accent-color rather than creating new local copies.
Built-in Variables
Variables that are defined by a built-in module cannot be modified. Attempting to reassign them will fail:
@use "sass:math" as math;
// This assignment will fail.
math.$pi: 0;Advanced Variable Functions
The sass:meta module provides two functions for inspecting variables:
| Function | Returns |
|---|---|
meta.variable-exists($name) |
Whether a variable with the given name exists in the current scope |
meta.global-variable-exists($name) |
Whether a variable exists in the global scope |
@use "sass:meta";
$primary: #2196f3;
@debug meta.variable-exists("primary"); // true
@debug meta.global-variable-exists("primary"); // true
@debug meta.variable-exists("undefined"); // falseSCSS vs. Sass Syntax
Variables work the same way in both syntaxes; only the punctuation differs (no semicolons in the indented syntax):
$base-color: #c6538c
$border-dark: rgba($base-color, 0.88)
.alert
border: 1px solid $border-darkCompiles to the same CSS as the SCSS version.
Summary
| Feature | Detail |
|---|---|
| Declaration | $name: value; (SCSS) or $name: value (Sass) |
| Hyphens/underscores | Treated as identical: $font-size == $font_size |
| Default flag | !default, only sets the value if the variable is undefined or null |
| Global flag | !global, assigns to the global scope from within a block |
| Scope | Top-level = global; inside blocks = local; flow control assigns to outer scope |
| Built-in variables | Cannot be modified (e.g. math.$pi) |
| Dynamic names | Not supported via interpolation: use a map instead |
Key takeaways:
- Define variables once at the top of your file and reuse them everywhere.
- Use
!defaultin libraries so users can override your values. - Remember that Sass variables are imperative (compiled away). Different from CSS variables.
- Mind scope: blocks are local, flow control rules assign to the outer scope.
- For dynamic lookups, use a map +
map.get(), not interpolated variable names.