TH The Sass Way
Sass & SCSS

Sass Variables: How to Declare, Scope, and Reuse Them

Sass Variables: How to Declare, Scope, and Reuse Them
tldrSass variables store reusable values — colors, sizes, font stacks — declared with a dollar sign, like $brand-color: #1f7a5e;. You reference them anywhere a CSS value goes, and at compile time Sass replaces each reference with its value, so the browser only sees plain CSS. Variables declared at the top level are global; those declared inside a block are local to it. The !default flag makes a variable overridable, which powers configurable libraries via @use ... with.

What Are Sass Variables?

A Sass variable is a named value — a color, a size, a font stack, almost anything — that you declare once with a $ prefix and reuse throughout your stylesheets. You declare one with $name: value; and reference it anywhere a value could go. When Sass compiles to CSS, every variable reference is replaced with its value, so the browser only ever sees plain CSS.

$brand-color: #1f7a5e;
$base-spacing: 1rem;

.button {
  background: $brand-color;
  padding: $base-spacing $base-spacing * 2;
}

Compiles to:

.button {
  background: #1f7a5e;
  padding: 1rem 2rem;
}

That's the whole core idea: define a value in one place, use it in fifty, change it in one. If your brand color shifts next quarter, you edit a single line instead of hunting through every file. Variables were the feature that first pulled most developers into Sass, and they're still the fastest win you get from it. If you're brand new to the language itself, start with what Sass is and why it exists, then come back here.

How to Declare a Sass Variable

The syntax is deliberately close to a CSS declaration:

$variable-name: value;

A few rules worth knowing up front:

What can a variable hold?

Sass variables aren't limited to colors. They can store any Sass data type:

Lists and maps are where variables graduate from "handy" to "architectural" — a single $breakpoints map can drive every media query on your site.

Sass Variable Scope: Global vs Local

Scope determines where a variable can be used. Sass has two levels:

$accent: #1f7a5e; // global

.card {
  $accent: #c0392b; // local — shadows the global inside .card
  border-color: $accent;
}

.footer {
  border-color: $accent; // still the global value
}

Compiles to:

.card {
  border-color: #c0392b;
}

.footer {
  border-color: #1f7a5e;
}

Notice what happened in .card: declaring a local variable with the same name as a global one doesn't overwrite the global. It shadows it — the local value wins inside that block, and the global value remains untouched everywhere else. This is a feature, not a bug: mixins and functions can use short internal variable names without worrying about trampling your globals.

Overriding a global from inside a block: !global

If you genuinely want an assignment inside a block to change the global variable, add the !global flag:

$theme: light;

@mixin activate-dark {
  $theme: dark !global;
}

Use this sparingly. Modern Dart Sass expects the global variable to already exist at the top level — using !global to invent a brand-new variable from inside a block is deprecated. In practice, code that needs !global often signals a structure that would be cleaner with maps or module configuration.

One quirk: flow control scope

Variables assigned inside @if, @each, @for, or @while behave differently: they can update a variable from the surrounding scope without !global, but a new variable first declared inside the flow-control block isn't visible outside it. So declare the variable before the loop, then modify it inside:

$total: 0;

@each $size in (8px 16px 24px) {
  $total: $total + $size;
}
// $total is now 48px

The !default Flag: Variables Made for Overriding

!default assigns a value to a variable only if that variable is undefined or null. If it already has a value, the assignment is skipped:

$accent: #7c4dff !default;

This one flag is the backbone of every configurable Sass library. Frameworks like Bootstrap declare hundreds of variables with !default so that your values, if you provide them, win over the library's fallbacks.

Configuring a module with @use ... with

In the modern module system, you override !default variables at import time:

// _theme.scss
$accent: #7c4dff !default;
$radius: 4px !default;

// main.scss
@use 'theme' with (
  $accent: #ff5722
);

Inside main.scss, theme.$accent is #ff5722 and theme.$radius keeps its default of 4px. Only variables marked !default can be configured this way — attempting to configure one without the flag is a compile error, which is exactly the guardrail you want in a shared codebase.

Sharing Variables Across Files with @use

Real projects keep variables in a dedicated partial and load it where needed. The modern way is @use, which namespaces everything it loads:

// _tokens.scss
$brand: #1f7a5e;
$radius: 6px;

// button.scss
@use 'tokens';

.button {
  background: tokens.$brand;
  border-radius: tokens.$radius;
}

The namespace (tokens.) tells you exactly where every value comes from — a genuine readability win in large codebases. If the prefix feels heavy, you can rename it (@use 'tokens' as t;) or drop it entirely (@use 'tokens' as *;), though the wildcard form gives up the main benefit.

Avoid the older @import for new code: it dumps every variable into one global soup, and the Sass team has deprecated it in favor of @use and @forward. Our complete guide to Sass covers structuring partials and modules in depth.

Sass Variables vs CSS Variables (Custom Properties)

CSS now has native variables, so which should you use? They solve different problems, and the honest answer for most projects is both.

Sass variables CSS custom properties
Syntax $name: value; --name: value; used via var(--name)
Resolved At compile time At runtime, in the browser
Exists in final CSS? No — replaced by values Yes — stays live in the cascade
Can JavaScript change it? No Yes
Responds to cascade/inheritance? No Yes — can differ per element or theme
Usable in media query conditions? Yes No
Can hold non-value logic (maps, lists for loops)? Yes No

The rule of thumb:

One gotcha when combining them: inside a custom property declaration, Sass treats the value as opaque, so you must interpolate:

$brand: #1f7a5e;

:root {
  --brand: #{$brand}; // interpolation required
}

Without #{ }, the compiled CSS would contain the literal text $brand. For the runtime side of this story, see our guide to CSS custom properties.

Patterns for Reusing Variables Well

A few habits that separate maintainable variable systems from sprawl:

When a variable starts needing logic — computing a contrast color, scaling a size — that's the moment to graduate to a function. Our comparison of Sass mixins vs functions covers when each tool earns its keep.

Common Mistakes to Avoid

Master these and variables stop being just a convenience — they become the contract that keeps a whole team's CSS speaking one language.

FAQ

How do you declare a variable in SCSS?

Write a dollar sign, a name, a colon, and a value: $brand-color: #1f7a5e;. Then use the variable anywhere a value could appear, such as background: $brand-color;. Declarations at the top level of a file are global and available everywhere after that point; declarations inside a selector, mixin, or function are local to that block. Hyphens and underscores in names are interchangeable, so $font-size and $font_size are the same variable.

What is the difference between Sass variables and CSS variables?

Sass variables are resolved at compile time and disappear from the final CSS — every reference is replaced by its value. CSS custom properties (--name) stay in the compiled stylesheet, participate in the cascade, and can be changed at runtime by JavaScript or per-element overrides. Use Sass variables for build-time values like breakpoints and loop data, and CSS variables for runtime concerns like theme switching. Many projects use both together.

What does the !default flag do in Sass?

The !default flag assigns a value to a variable only if that variable is currently undefined or null; otherwise the assignment is skipped. Library authors mark their variables with !default so consumers can override them, typically at load time with @use 'library' with ($accent: #ff5722). Only variables declared with !default can be configured this way — trying to configure any other variable is a compile error.

How does variable scope work in Sass?

Variables declared at the top level of a stylesheet are global and usable anywhere after the declaration. Variables declared inside a block — a selector, mixin, or function — are local and only exist within that block. Declaring a local variable with the same name as a global one shadows the global inside that block without changing it elsewhere. To deliberately reassign a global from inside a block, add the !global flag.

Can you use a Sass variable inside a CSS custom property?

Yes, but you must use interpolation. Inside a custom property declaration Sass treats the value as opaque text, so --brand: $brand; would output the literal string "$brand". Wrap the variable in interpolation instead: --brand: #{$brand};. This pattern is common for theming — Sass holds the design tokens at build time, and the interpolated custom properties expose them to the browser at runtime.

How do you share Sass variables between files?

Put them in a partial such as _tokens.scss and load it with @use 'tokens'; then reference values through the namespace, like tokens.$brand. You can shorten the namespace with @use 'tokens' as t, or remove it with as *, though the namespace is worth keeping for readability. Avoid the older @import rule for new code — it is deprecated in favor of @use and @forward.