Skip to content

Sass Variables

How to use Sass variables to store reusable values like colors and font sizes in your stylesheets, improving maintainability and consistency.

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.css

The 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");         // false

SCSS 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-dark

Compiles 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:

  1. Define variables once at the top of your file and reuse them everywhere.
  2. Use !default in libraries so users can override your values.
  3. Remember that Sass variables are imperative (compiled away). Different from CSS variables.
  4. Mind scope: blocks are local, flow control rules assign to the outer scope.
  5. For dynamic lookups, use a map + map.get(), not interpolated variable names.
Navigation