CSSSass Variables & Nesting

Sass Variables & Nesting

Variables and nesting were two of the original reasons developers reached for Sass in the first place — see Sass / SCSS Introduction for the wider context. Both features are still used constantly in real Sass codebases today, even now that native CSS has its own answers to similar problems.
Sass variables: $variable

A Sass variable is declared with a leading dollar sign and can hold any Sass value — a color, a number, a string, a list, or a map.

SCSS
$brand-color: #0066cc;
$spacing-unit: 8px;
$font-stack: 'Helvetica Neue', Arial, sans-serif;

.button {
  background: $brand-color;
  padding: $spacing-unit * 2;
  font-family: $font-stack;
}

Once compiled, every reference to the variable is replaced with its literal value — the browser only ever sees the final numbers and colors, with no trace of the variable name left behind.

CSS
/* Compiled output */
.button {
  background: #0066cc;
  padding: 16px;
  font-family: 'Helvetica Neue', Arial, sans-serif;
}
Sass variables vs. CSS custom properties
It's tempting to treat Sass variables as a direct equivalent of native CSS custom properties (--variable), but the two work at fundamentally different times and have very different capabilities.

Sass $variable

CSS --custom-property

When it exists

Only at compile time — resolved away entirely before the browser sees the CSS.

At runtime, in the browser — a real, live value the page can read and change.

Can it change via JavaScript?

No — there is nothing left to change once compilation has happened.

Yes — element.style.setProperty() updates it immediately.

Can it change per media query / breakpoint?

No — a single compiled value is baked into the output for a given selector.

Yes — redeclaring it inside a @media block changes it live for matching viewports.

Scope

Sass scoping rules (roughly block-scoped, depending on where declared).

Follows the CSS cascade and inheritance, scoped to the elements it is declared on.

This distinction genuinely matters, not just trivia
Because a Sass variable disappears at compile time, it cannot power a runtime dark-mode toggle, a JavaScript-driven theme switcher, or a value that changes based on a media query condition evaluated by the browser. Projects that need any of that reach for CSS custom properties instead — many real codebases use both together: Sass variables for build-time constants, custom properties for anything that needs to be live.
Nesting selectors
Sass nesting predates native CSS nesting by well over a decade, and was one of the most-loved Sass features — it lets you write a child selector inside its parent's block instead of repeating the parent selector on a separate line.

SCSS
.card {
  padding: 1rem;
  border: 1px solid #ddd;

  .card-title {
    font-size: 1.25rem;
    font-weight: 600;
  }

  .card-body {
    color: #555;
  }
}

which compiles to:

CSS
.card {
  padding: 1rem;
  border: 1px solid #ddd;
}
.card .card-title {
  font-size: 1.25rem;
  font-weight: 600;
}
.card .card-body {
  color: #555;
}
The & parent selector
Inside a nested block, & refers back to the parent selector, which is essential for attaching a pseudo-class, pseudo-element, or a modifier class directly to the parent rather than selecting a descendant of it.

SCSS
.button {
  background: #0066cc;
  color: white;

  &:hover {
    background: #0052a3;
  }

  &.is-disabled {
    opacity: 0.5;
  }

  // BEM-style modifier built with & and string interpolation
  &--large {
    padding: 1rem 2rem;
    font-size: 1.125rem;
  }
}

compiles to:

CSS
.button {
  background: #0066cc;
  color: white;
}
.button:hover {
  background: #0052a3;
}
.button.is-disabled {
  opacity: 0.5;
}
.button--large {
  padding: 1rem 2rem;
  font-size: 1.125rem;
}
Sass nesting rules are slightly looser than native nesting
Sass has always allowed a plain nested selector like &--large or interpolated names built with #{$variable}, patterns native CSS nesting has no equivalent for. See CSS Nesting for a direct comparison of where the two diverge.
Putting it together

SCSS
$border-radius: 8px;
$muted-text: #6b7280;

.card {
  padding: 1rem;
  border-radius: $border-radius;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);

  &__title {
    font-weight: 600;
  }

  &__meta {
    color: $muted-text;
    font-size: 0.875rem;
  }

  &:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  }
}
  • $variable: value; declares a compile-time value, resolved and inlined before shipping.

  • Nesting mirrors HTML structure visually, but every level compiles to a fully qualified flat selector.

  • & stands in for the parent selector wherever it appears — directly attached, or combined with a suffix for BEM-style modifiers.

Next
See how to package repeated declaration blocks with Sass Mixins & Functions.