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

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:
- Variables start with
$.$primary,$gutter-width,$font-stack— the dollar sign is what tells Sass this is a variable, not a property. - Hyphens and underscores are interchangeable.
$font-sizeand$font_sizerefer to the same variable. This is a historical quirk; pick hyphens and stay consistent. - Declarations need a value. Unlike some languages, you can't declare an empty variable — but you can assign
null, and a property whose value resolves tonullis simply omitted from the compiled CSS.
What can a variable hold?
Sass variables aren't limited to colors. They can store any Sass data type:
- Numbers, with or without units:
$columns: 12;,$gutter: 24px; - Strings:
$font-stack: "Inter", sans-serif; - Colors:
$danger: #c0392b; - Booleans:
$enable-shadows: true; - Null:
$border: null; - Lists:
$sizes: 8px 16px 24px; - Maps (key–value pairs):
$breakpoints: (small: 480px, medium: 768px, large: 1024px);
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:
- Global variables are declared at the top level of a file, outside any selector or block. They're available everywhere after the point of declaration.
- Local variables are declared inside a block — a selector, mixin, function, or other
{ }— and only exist within that block and its children.
$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:
- Sass variables for values fixed at build time: breakpoints, grid math, values you loop over, anything feeding Sass functions or mixins.
- CSS custom properties for values that change at runtime: theme switching, user preferences, per-component overrides.
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:
- Name by role, not appearance.
$color-dangersurvives a redesign;$redbecomes a lie the day danger turns orange. - Layer your tokens. Define raw values first (
$green-600: #1f7a5e;), then map them to roles ($color-success: $green-600;). Components reference roles only. - Reach for maps when variables multiply. Ten
$spacing-*variables are a map wanting to happen.map.get($spacing, "md")with a loop generates utility classes in five lines. - Keep math in variables, not selectors.
$gutter-half: $gutter / 2in one place beats repeating the expression — and note that modern Dart Sass prefersmath.div($gutter, 2)over the/operator for division.
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
- Using a variable before declaring it. Sass reads top to bottom; a reference above the declaration is a compile error.
- Expecting a global update from a plain local assignment. Inside a block,
$x: value;shadows; it doesn't overwrite. Use!globalor restructure. - Forgetting interpolation in selectors, property names, and custom property values:
.icon-#{$name}works,.icon-$namedoesn't. - Skipping
!defaultin shared partials. If teammates can't override your variables at@usetime, your "design tokens" are hard-coded constants.
Master these and variables stop being just a convenience — they become the contract that keeps a whole team's CSS speaking one language.