Sass/SCSS Variables Tutorial

In this Sass/SCSS tutorial we learn about temporary data storage containers called variables.

We cover how to create variables with and without values, how to use them, change their values and set default fallback values.

We also discuss local and global variable scope and shadowing, as well as the difference between Sass variables and CSS custom properties.

What is a variable

A variable is a reusable container that we can store data inside of.

As an example, consider how many times we use a color throughout a stylesheet. If we need to change that color at some point, we’ll need to change it in all those places.

A variable allows us to define the color once in a single place and then refer to the variable throughout our stylesheet.

If we need to come back later and change the color, we only change the value of the variable and it will reflect that change throughout the entire stylesheet.

How to initialize a variable with a value

To create a variable we start with the dollar symbol, followed by one or more descriptive words as the variable name. Then we assign a value to it by specifying the colon operator, followed by the value we want to store inside the variable.

Syntax:
$variable_name: value;
Example:
$col-background: #000;

Although variables can be defined anywhere in a stylesheet, they are usually grouped at the top of the document or in a dedicated separate file.

How to define a variable without a value

It’s possible to define a variable without an initial value. This is useful if we want to assign a value to it later on, or conditionally for example.

To create an empty variable, we simply assign the null value to it.

Syntax:
$variable_name: null;

How to use a variable

To use a variable, we refer to its name wherever we need in the stylesheet.

Example:
$col-background: #000;

body {
  background: $col-background;
}

In the example above we use the variable as the value for the background property.

What will happen is that the compiler will replace $col-background with #000 everywhere in the document.

How to change the value of a variable

To change the value of a variable, we replace the current value with a new one.

Example:
$col-background: #fff;

body {
  background: $col-background;
}

If we now look at the HTML in the browser, the background color will have changed from black to white.

How to set a !default fallback value

Sass allows us to define a default value for a variable that acts as a fallback value in case it wasn’t set higher up in the load order.

To set a default value, we mark a variable value with !default , the same way we would use !important in standard CSS.

Syntax:
$variable_name: value !default;

Let’s consider a simple example.

Example:
$col-background: #fff !default;

body {
  background: $col-background;
}

The color is generated as #fff because there is no other value for the variable, so it defaults to this one.

Example: Compiled CSS
body {
  background: #fff;
}

But, if we set a different value higher up in the load order, that value will be used instead.

Example:
$col-background: red;

$col-background: #fff !default;

body {
  background: $col-background;
}

The !default value will only be used when the variable doesn’t already have a value defined. It acts as a fallback.

Output: Compiled CSS
body {
  background: red;
}

This can make our code a lot more modular and even help us share code between projects.

Many CSS frameworks that use Sass will !default a lot of their variables. There will typically be a dedicated file where you can set custom values if you want. But if you don’t set any, the code can’t break because those values have defaults.

Variable scope

Sass variables that are initialized outside the scope of a selector, is part of the global scope and can be used anywhere in the document.

Sass variables that are initialized inside the scope of a selector can only be used within that selector.

Example:
// Global scope variable
$col-bg: #000;

html {
  // Local scope variable
  $col-text: #fff;

  color: $col-text;
}

body {
  background: $col-bg;

  // If we try to use it globally
  // the compiler will raise an error
  color: $col-text;
}

The local variable $col-text is only available to be used inside html .

When we try to use it inside body , the compiler raises an error.

Output:
Error: Undefined variable.

Sass allows us to define variables with the same name in both the global and local scope without them conflicting. This practice is known as shadowing.

Example:
// Global scope variable
$col-bg: #000;

html {
  // Local scope variable
  $col-bg: #fff;
  background: $col-bg;
}

body {
  background: $col-bg;
}

In the examples above, we define both variables with the name $col-bg. Because one is local and the other is global, the compiler doesn’t raise an error.

Operands: Lvalues & Rvalues

Sass has two types of expressions, lvalues and rvalues.

The operand on the left side of the assignment operator ( = ) is the Lvalue, and the operand on the right side is the Rvalue.

Lvalue:

An Lvalue can have a value assigned to it so it’s allowed to appear either on the left or the right side of an assignment.

Example: lvalue
// lvalue (num1) may
// be on the left
$num1 = 10;

// or on the right of the =
$num2 = $num1;

Rvalue:

An rvalue cannot have a value assigned to it so it may only appear on the right side of an assignment.

Example: rvalue
// rvalue (10) may only be on the right
$num1 = 10;

// and cannot be on the left
10 = 10;

Sass/SCSS variables vs CSS custom properties

Although Sass variables and CSS custom properties (also known as CSS variables) seem to do the same thing, they are in fact very different.

Sass variables are static, which means they’ll be compiled away. They also have no knowledge of the DOM and don’t affect it.

Example: SCSS
$breakpoint: 1024px;

$col-primary: #2e647e;
$col-secondary: #f7fafc;

$shadow-dp-1: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);

$af-primary: "body, .button";
$af-secondary: ".card";

@media screen and (min-width: $breakpoint) {
  #{$af-primary} {
    background: $col-primary;
  }

  #{$af-secondary} {
    background: $col-secondary;
    box-shadow: $shadow-dp-1;
  }
}

The code in the example above will be compiled into the CSS below.

Example: CSS
@media screen and (min-width: 1024px) {
  body, .button {
    background: #2e647e;
  }

  .card {
    background: #f7fafc;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  }
}

The variables from the Sass script are gone, only the values exist in the CSS document.

CSS variables are dynamic, they’re not compiled away like Sass variables. But, some of the examples above would be invalid using custom properties.

Example: CSS
:root {
  --col-primary: #2e647e;
  --col-secondary: #f7fafc;
  --shadow-dp-1: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}

@media screen and (min-width: 1024px) {
  body, .button {
    background: var(--col-primary);
  }

  .card {
    background: var(--col-secondary);
    box-shadow: var(--shadow-dp-1);
  }
}

That’s because custom properties have the same rules about where they can be used as standard CSS properties.

CSS variables can only be used inside a declaration block, in other words, they’re tied to a selector. Sass variables are not.

Because the CSS variable is tied to a selector, it can affect DOM elements just like any other CSS property.

This means we can change the value of a custom property at runtime with a pseudo selector like hover, or even with Javascript, while still keeping a separation of concerns.

When to use Sass variables vs CSS custom properties

This is mostly up to personal preference and needs. However, a good rule of thumb is to avoid doing things at runtime that can be done at compile time.

Use Sass variables for static values and CSS custom properties for dynamic values. We shouldn’t give the browser extra work at runtime when it isn’t necessary.

As an example, let’s consider the color theme of a website. It’s unlikely to change for a very long time so it’s the perfect candidate for static values like Sass variables.

They don’t need to be dynamic, they don’t need to affect the DOM. Another benefit we gain from this is that we can use Sass functions like lighten() and darken() .

Example: HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="css/style.css">
  <title>Document</title>
</head>
<body>
  <div>
    <div>600</div>
    <div>500</div>
    <div>400</div>
  </div>
</body>
</html>
Example: SCSS
$col-primary: #2e647e;

$col-primary-400: lighten($col-primary, 10%);
$col-primary-500: $col-primary;
$col-primary-600: darken($col-primary, 10%);

div > div {
  padding: 2rem;
}

div:nth-child(1) {
  background: $col-primary-600;
}

div:nth-child(2) {
  background: $col-primary-500;
}

div:nth-child(3) {
  background: $col-primary-400;
}

On the other hand, if we wanted to have a switchable theme with light and dark mode on a website, we would have to use dynamic values that could change at runtime. In this situation, CSS custom properties are the better solution.

Example: HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="css/style.css">
  <title>Document</title>
</head>
<body>
  <input id="theme-switch" class="theme-switch" type="checkbox" />
  <label for="theme-switch" class="theme-switch-label">Light/Dark</label>
  <div>
    <div>600</div>
    <div>500</div>
    <div>400</div>
  </div>

<script>
  const toggleSwitch = document.getElementById('theme-switch');
  const currentTheme = localStorage.getItem('theme');

  if (currentTheme) {
    document.documentElement.setAttribute('data-theme', currentTheme);

    if (currentTheme === 'dark') {
      toggleSwitch.checked = true;
    }
  }

  function switchTheme(e) {
    if (e.target.checked) {
      document.documentElement.setAttribute('data-theme', 'dark');
      localStorage.setItem('theme', 'dark');
    }
    else {
      document.documentElement.setAttribute('data-theme', 'light');
      localStorage.setItem('theme', 'light');
    }
  }

  toggleSwitch.addEventListener('change', switchTheme, false);
</script>
</body>
</html>
Example: CSS
:root {
  --col-text: #191919;
  --col-primary-400: #e6e6e6;
  --col-primary-500: #ccc;
  --col-primary-600: #b3b3b3;
}

[data-theme="dark"] {
  --col-text: #dedede;
  --col-primary-400: #3c82a3;
  --col-primary-500: #2e647e;
  --col-primary-600: #204659;
}

.theme-switch {
  margin-bottom: 2rem;
}

div > div {
  padding: 2rem;
  color: var(--col-text);
}

div:nth-child(1) {
  background: var(--col-primary-600);
}

div:nth-child(2) {
  background: var(--col-primary-500);
}

div:nth-child(3) {
  background: var(--col-primary-400);
}

We don’t always necessarily need to pick one or the other. We can use interpolation and combine Sass variables with CSS custom properties.

We can wrap the Sass variable in #{} and use it as the custom property value.

Example: SCSS
$col-primary: #ccc;

$col-primary-400: lighten($col-primary, 10%);
$col-primary-500: $col-primary;
$col-primary-600: darken($col-primary, 10%);

$col-secondary: #2e647e;

$col-secondary-400: lighten($col-secondary, 10%);
$col-secondary-500: $col-secondary;
$col-secondary-600: darken($col-secondary, 10%);

:root {
  --col-text: #191919;
  --col-primary-400: #{$col-primary-400};
  --col-primary-500: #{$col-primary-500};
  --col-primary-600: #{$col-primary-600};
}

[data-theme="dark"] {
  --col-text: #dedede;
  --col-primary-400: #{$col-secondary-400};
  --col-primary-500: #{$col-secondary-500};
  --col-primary-600: #{$col-secondary-600};
}

.theme-switch {
  margin-bottom: 2rem;
}

div > div {
  padding: 2rem;
  color: var(--col-text);
}

div:nth-child(1) {
  background: var(--col-primary-600);
}

div:nth-child(2) {
  background: var(--col-primary-500);
}

div:nth-child(3) {
  background: var(--col-primary-400);
}

Summary: Points to remember

  • A Sass variable is a data container that can hold any value.
  • A Sass variable name is prefixed with the dollar $ symbol.
  • A Sass variable must be initialized with a value.
  • To change the value, we simply replace the old value with a new one.
  • A variable that’s initialized inside a selector can only be used within that selector’s scope.
  • A variable that’s initialized outside a selector can be used anywhere in the document.
  • Sass has two types of operands
    • Lvalues are the operands on the left and can be assigned a value.
    • Rvalues are the operands on the right and cannot be assigned a value.
  • Sass variables and CSS custom properties are not the same.
    • Sass variables are static and are compiled away. They can be used anywhere in the document and doesn’t affect the DOM.
    • CSS custom properties are tied to a selector and can affect the DOM at runtime.
  • Use Sass variables in situations where you want static values and CSS custom properties in situations where you want dynamic values.
    • Sass variables can be used as CSS custom property values by wrapping the Sass variable in #{} .