Episode 1 — Fundamentals / 1.7 — Working With SASS
1.7.c — Variables & Nesting
In one sentence: SCSS variables (
$name: value) let you define values once and reuse them everywhere, while nesting lets you write selectors that mirror your HTML structure — but keep nesting shallow to avoid specificity nightmares.
Navigation: ← 1.7.b — SCSS vs SASS & Setup · 1.7.d — Partials & Imports →
1. SCSS variables
Declaring and using
$primary: #3b82f6;
$font-stack: 'Inter', sans-serif;
$spacing-md: 1rem;
.btn {
background: $primary;
font-family: $font-stack;
padding: $spacing-md;
}
Compiles to:
.btn {
background: #3b82f6;
font-family: "Inter", sans-serif;
padding: 1rem;
}
Variables are replaced at compile time — the browser never sees $primary.
2. Data types
| Type | Example | Notes |
|---|---|---|
| Number | 16px, 1.5, 100% | With or without units |
| String | 'Inter', bold, "hello" | Quoted or unquoted |
| Color | #3b82f6, rgb(59,130,246), blue | Any CSS color format |
| Boolean | true, false | Used in @if conditions |
| Null | null | Property is omitted from output |
| List | 1rem 2rem 3rem, (a, b, c) | Space or comma separated |
| Map | ('sm': 640px, 'md': 768px) | Key-value pairs in parentheses |
3. Variable scope
$color: red; // global
.card {
$color: blue; // local — only inside .card block
background: $color; // blue
}
.footer {
color: $color; // red (global)
}
Use the !global flag to override the global variable from inside a block — but avoid this; it makes code hard to trace.
.card {
$color: blue !global; // now global $color is blue everywhere after this point
}
!default — safe defaults
$primary: #3b82f6 !default;
"Use #3b82f6 only if $primary is not already defined." This is how libraries let consumers override theme variables before importing.
4. Nesting selectors
Basic nesting
.nav {
background: #1e293b;
ul {
list-style: none;
display: flex;
}
a {
color: white;
text-decoration: none;
}
}
Compiles to:
.nav { background: #1e293b; }
.nav ul { list-style: none; display: flex; }
.nav a { color: white; text-decoration: none; }
5. The & parent selector
& refers to the entire parent selector at that point in the nesting. It is essential for pseudo-classes, BEM naming, and modifier patterns.
.btn {
background: $primary;
&:hover {
background: darken($primary, 10%);
}
&:focus-visible {
outline: 2px solid $primary;
}
&--large {
padding: 1rem 2rem;
font-size: 1.25rem;
}
&__icon {
margin-right: 0.5rem;
}
}
Compiles to:
.btn { background: #3b82f6; }
.btn:hover { background: #2563eb; }
.btn:focus-visible { outline: 2px solid #3b82f6; }
.btn--large { padding: 1rem 2rem; font-size: 1.25rem; }
.btn__icon { margin-right: 0.5rem; }
Without &, nesting would produce .btn :hover (descendant) instead of .btn:hover (pseudo-class on same element).
6. Nesting properties
SCSS also allows nesting property namespaces (less commonly used):
.card {
font: {
family: 'Inter', sans-serif;
size: 1rem;
weight: 400;
}
}
// Outputs: font-family, font-size, font-weight
7. Nesting best practices
| Rule | Why |
|---|---|
| Max 3 levels deep | Deeper nesting produces long selectors with high specificity — hard to override |
| Avoid nesting element selectors blindly | .sidebar div p span breaks when HTML structure changes |
Use & for pseudo-classes and BEM | Keeps related styles co-located without deep nesting |
| Flatten when selectors get long | If the compiled selector is unreadable, unnest |
Bad vs good
// ❌ Too deep — compiled selector: .page .sidebar .widget .title span
.page {
.sidebar {
.widget {
.title {
span { color: red; }
}
}
}
}
// ✅ Flat, intentional nesting
.widget__title {
span { color: red; }
}
8. SCSS variables vs CSS custom properties
SCSS Variables ($var) | CSS Custom Properties (--var) | |
|---|---|---|
| Resolved at | Compile time | Runtime (browser) |
| Available in | SCSS files only | CSS, JavaScript, DevTools |
| Cascade / inherit | No — static replacement | Yes — cascade and inherit through DOM |
| Dynamic changes | No (requires recompile) | Yes — change with JS or media queries |
| Use case | Build-time constants (breakpoints, base sizes) | Theme switching, dark mode, per-component overrides |
Best practice: Use SCSS variables for values the compiler needs (breakpoints in @if, loop bounds). Use CSS custom properties for values the browser needs to change (dark mode toggle, user preferences).
$breakpoint-md: 768px; // compiler uses this in @media
:root {
--color-primary: #{$primary}; // browser toggles this for dark mode
}
9. Key takeaways
$variable: value;— defined once, used everywhere, resolved at compile time.- SCSS has seven data types — numbers, strings, colors, booleans, null, lists, and maps.
- Nesting mirrors HTML structure but keep it to 3 levels max to avoid specificity problems.
&is the parent selector — essential for pseudo-classes (:hover), pseudo-elements (::before), and BEM modifiers (&--active).- SCSS variables and CSS custom properties solve different problems — use both where appropriate.
Explain-It Challenge
Explain without notes:
- What happens to
$primaryin the final CSS — does the browser see it? - Why is deeply nested SCSS (4+ levels) considered an anti-pattern?
- When would you prefer a CSS custom property (
--var) over a SCSS variable ($var)?
Navigation: ← 1.7.b — SCSS vs SASS & Setup · 1.7.d — Partials & Imports →