Variables

A Sass variable stores a value once under a name starting with $, so a brand color or a spacing unit only has to be written down in one place — everywhere else just refers to the name.

Declaring and using a variable

SCSS styles.scss
$primary-color: #1b6ec2;
$spacing-unit: 8px;

.button {
  background: $primary-color;
  padding: $spacing-unit * 2;
  border: 1px solid $primary-color;
}
Compiled CSS
.button {
  background: #1b6ec2;
  padding: 16px;
  border: 1px solid #1b6ec2;
}

By the time this reaches a browser, $primary-color and $spacing-unit don't exist anymore — Sass has substituted their actual values everywhere they were used, including inside the math expression $spacing-unit * 2, which resolves to 16px before the CSS is written out.

Changing a value in one place

This is the entire point: change the variable once, and every rule that referenced it updates on the next compile.

SCSS styles.scss
$primary-color: #d6336c;

.button {
  background: $primary-color;
}
.link {
  color: $primary-color;
}
.badge {
  border-color: $primary-color;
}
Compiled CSS
.button {
  background: #d6336c;
}
.link {
  color: #d6336c;
}
.badge {
  border-color: #d6336c;
}

Three rules, one source of truth — swapping $primary-color to a different hex value updates all three the next time the file compiles, with no find-and-replace required.

Variable scope

A variable declared at the top level of a file is global and visible everywhere below it. One declared inside a selector block or a mixin is local to that block by default:

SCSS styles.scss
$size: 16px;

.card {
  $size: 24px;
  font-size: $size;
}

.label {
  font-size: $size;
}
Compiled CSS
.card {
  font-size: 24px;
}
.label {
  font-size: 16px;
}

.card's local $size shadows the global one only inside that block — .label, outside the block, still sees the original global value of 16px. If you genuinely need to reassign a global variable from inside a nested block, Sass has a !global flag for exactly that, but reaching for it is rare and usually a sign the code could be restructured more clearly.

Note: this shadowing behavior trips people up constantly when a locally-scoped variable happens to share a name with a global one — the local value silently wins inside its block, with no warning. Keeping variable names specific ($card-size rather than a generic $size reused everywhere) avoids the collision entirely.