Intro to Sass — Variables, Nesting, Mixins
The CSS preprocessor that came before CSS variables — nesting, mixins, and why many real codebases still use it today.
Sass Compiles to Plain CSS — Nothing More, Nothing Less
Sass (Syntactically Awesome StyleSheets) is a preprocessor — you write files in Sass's own extended syntax (.scss), and a build step compiles them into plain, ordinary CSS that ships to the browser. The browser itself has no idea Sass was ever involved; it only ever receives standard CSS.
$primary-color: #4285f4;
.button {
background: $primary-color;
padding: 12px 20px;
}.button {
background: #4285f4;
padding: 12px 20px;
}Compile-Time vs Runtime — the Real Distinction
The Custom Properties module covered native --variable/var() CSS variables in depth. Sass's $variable syntax looks similar but works completely differently under the hood — the difference is genuinely important, not cosmetic.
$spacing-unit: 8px;
.card {
padding: $spacing-unit * 2; // compiles to a fixed "padding: 16px;" — done, forever
}:root {
--spacing-unit: 8px;
}
.card {
padding: calc(var(--spacing-unit) * 2);
/* Still "padding: 16px" visually — but the browser can genuinely
recompute this if --spacing-unit changes later via JavaScript
or a media query, with zero rebuild step involved */
}Writing Selectors That Mirror Your HTML Structure
.card { padding: 16px; }
.card .title { font-weight: 700; }
.card .title:hover { color: #4285f4; }
.card .footer { border-top: 1px solid #ddd; }.card {
padding: 16px;
.title {
font-weight: 700;
&:hover {
color: #4285f4;
}
}
.footer {
border-top: 1px solid #ddd;
}
}The & symbol refers to the immediate parent selector — &:hover compiles to .title:hover, not a new descendant selector. Nesting genuinely mirrors the visual/structural relationship in your HTML, which can make a stylesheet easier to navigate.
Reusable Blocks of Styles, With Parameters
A mixin is a named, reusable block of CSS declarations — optionally accepting arguments — that gets pasted inline wherever it's included, similar in spirit to a function.
@mixin flex-center($direction: row) {
display: flex;
align-items: center;
justify-content: center;
flex-direction: $direction;
}
.hero {
@include flex-center;
}
.sidebar {
@include flex-center($direction: column);
}.hero {
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
}
.sidebar {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}The $direction: row default parameter value means @include flex-center; with no arguments still works, falling back to row — exactly the same default-parameter idea that shows up in most programming languages.
Splitting a Large Stylesheet Into Organized Files
// _variables.scss
$primary-color: #4285f4;
$spacing-unit: 8px;
// _mixins.scss
@mixin flex-center { display: flex; align-items: center; justify-content: center; }
// main.scss
@use 'variables' as v;
@use 'mixins' as m;
.card {
padding: v.$spacing-unit * 2;
@include m.flex-center;
}Files prefixed with an underscore (_variables.scss) are partials — they are never compiled to their own separate CSS output file, only ever pulled into another file via @use. This is directly the same organizational instinct as the CSS Architecture module's advice on splitting large stylesheets into logical files.
Compile-Time Logic Native CSS Still Cannot Do
Native CSS has genuinely closed much of the historical gap Sass filled — custom properties cover many of the old variable use cases, and nesting itself is now landing natively in CSS in modern browsers. What native CSS still cannot do, and what keeps Sass relevant in many real production codebases: mixins with real parameterized logic, @if/@each control-flow directives for generating repetitive CSS programmatically, and mathematical operations resolved entirely at build time with zero runtime cost.
@each $size in (4, 8, 12, 16, 24, 32) {
.p-#{$size} { padding: #{$size}px; }
}
// Generates six complete, separate CSS rules — .p-4, .p-8, .p-12, etc. —
// from six lines of Sass, with no runtime cost or JavaScript involvedThe #{$size} syntax above is Sass's interpolation — it drops a variable's value directly into a selector name or property value at compile time, something plain CSS custom properties cannot do at all (a custom property can only be used as a value, never spliced into a selector or property name itself).
A Utility Class System Generated in 20 Lines, at a Chicago Design Agency
A team building a shared design system needs a full spacing utility class set — margin and padding classes for every direction (top/right/bottom/left/all) across a defined spacing scale. Written by hand in plain CSS, that's dozens of nearly-identical rules to maintain.
$spacing-scale: (0, 4, 8, 12, 16, 24, 32, 48, 64);
$directions: (t: top, r: right, b: bottom, l: left);
@each $size in $spacing-scale {
.p-#{$size} { padding: #{$size}px; }
.m-#{$size} { margin: #{$size}px; }
@each $short, $full in $directions {
.p#{$short}-#{$size} { padding-#{$full}: #{$size}px; }
.m#{$short}-#{$size} { margin-#{$full}: #{$size}px; }
}
}What this actually saved
Roughly 90 individual CSS rules get generated from these 12 lines of Sass — and changing the spacing scale later (adding a new value, removing one) is a single-line edit to $spacing-scale rather than manually adding or removing dozens of hand-written rules. The team's own note: "this is exactly the kind of repetitive, mechanical generation work that a preprocessor is genuinely still better at than native CSS today."
Four Misconceptions About Sass
5 Interview Questions — With Complete Answers
Sass Mistakes Beginners Make Constantly
Issues You Will Hit With Sass — And Exactly Why
🎯 Key Takeaways
- ✓Sass compiles to plain CSS at build time — the browser never sees Sass syntax directly, only the compiled output.
- ✓The real distinction from CSS custom properties: Sass $variables are resolved at BUILD time and baked in permanently; custom properties are resolved at RUNTIME and can respond to live browser conditions.
- ✓Nesting mirrors your HTML structure but compiles to increasingly specific selectors as depth increases — keep it to roughly 2-3 levels in production code.
- ✓Mixins (@mixin/@include) are reusable, optionally parameterized blocks of CSS declarations, pasted inline wherever included.
- ✓Partial files (prefixed with an underscore) are never compiled to their own standalone output — they exist only to be pulled into another file via @use.
- ✓Sass remains genuinely useful today for what native CSS still cannot do: parameterized mixin logic, @each/@if-driven generation of repetitive rules, and compile-time interpolation into selector names.
What comes next
Phase 6 begins here — Production & Career Readiness, starting with responsive images and the performance techniques every real production page needs.
Module 37 → Responsive Images & PerformanceDiscussion
0Have a better approach? Found something outdated? Share it — your knowledge helps everyone learning here.