Sass / SCSS: The CSS Preprocessor
Sass is CSS with superpowers — variables, nesting, reusable mixins and the ability to split your styles into many small files. The best-selling paid CSS courses are built on it, and many real codebases still expect it.
What you will learn
- Explain what a preprocessor is and why Sass exists
- Use variables, nesting, partials and @use
- Write a reusable mixin and use @extend
What is a preprocessor?
A browser only understands plain CSS — it has never heard of Sass. So Sass is a language you write, then a tool compiles it into normal CSS that the browser can read. That compile step is why Sass is called a preprocessor: it runs before the CSS reaches the browser.
Sass (Syntactically Awesome Style Sheets) gives you tools plain CSS historically lacked: real variables, nesting, reusable chunks called mixins, math, and the ability to split styles across many files. The modern, CSS-like syntax uses the .scss file extension (the one everyone uses today), so people say “Sass” and “SCSS” to mean the same thing.
Note: Because Sass needs compiling, its code does not run live in the browser preview — so the examples below are marked as Sass input with the plain CSS they produce shown as an Output note. You compile it with a tool (covered at the end).
Variables and nesting
Sass variables start with a $. Nesting lets you write child rules inside a parent, just like the native nesting you saw earlier. Here is some SCSS:
// styles.scss (Sass input — gets compiled to CSS)
$brand: #4338ca;
$radius: 12px;
.card {
border: 1px solid #e6e8f0;
border-radius: $radius;
padding: 16px;
h3 { color: $brand; } // becomes .card h3
&:hover { box-shadow: 0 10px 24px rgba(20,24,48,.12); } // becomes .card:hover
}Read it piece by piece:
$brandand$radiusare Sass variables — named values you set once and reuse. (Sass also predates CSS variables, which is why older codebases use$heavily.)- The
h3 { ... }rule is nested inside.card, so it compiles to.card h3. &:hoveruses&(“the current selector”) to compile to.card:hover— exactly like native nesting.
/* The compiled CSS the browser actually receives */
.card { border: 1px solid #e6e8f0; border-radius: 12px; padding: 16px; }
.card h3 { color: #4338ca; }
.card:hover { box-shadow: 0 10px 24px rgba(20,24,48,.12); }Notice the variables are gone — Sass has substituted their values ($radius became 12px, $brand became #4338ca) — and the nesting has been flattened into ordinary descendant selectors. This compiled CSS is what the browser loads.
Mixins — reusable blocks of CSS
A mixin is a named, reusable group of declarations — like a function for CSS. You define it once with @mixin and drop it into any rule with @include. It can even take parameters. This is the single most loved Sass feature:
// Define a reusable mixin (it can take arguments)
@mixin flex-center($gap: 0) {
display: flex;
justify-content: center;
align-items: center;
gap: $gap;
}
.hero { @include flex-center(12px); height: 200px; }
.toolbar { @include flex-center; }The @mixin flex-center($gap: 0) bundles the three-line flexbox-centring recipe (which you learned by hand in the Centering lesson) into one reusable name, with a $gap argument that defaults to 0. Then @include flex-center(12px) pastes those declarations in with a 12px gap, and @include flex-center pastes them with the default gap. Here is what it compiles to:
/* Compiled output — the mixin is expanded into each rule */
.hero { display: flex; justify-content: center; align-items: center; gap: 12px; height: 200px; }
.toolbar { display: flex; justify-content: center; align-items: center; gap: 0; }The mixin’s declarations have been copied into each rule that included it, with the argument filled in. Write the centring recipe once, reuse it anywhere — and if you improve the mixin, every place that includes it updates.
Partials and @use — splitting your CSS into files
Big stylesheets get unwieldy. Sass lets you split CSS into small files called partials (their names start with an underscore, like _buttons.scss) and then pull them together. The modern way to combine files is the @use rule. The process:
- Create small partials, each for one concern:
_variables.scss,_buttons.scss,_cards.scss. - In a main file (
main.scss), bring them in with@use: for example@use "buttons";and@use "cards";. - Compile
main.scss— Sass stitches every partial into one CSS file the browser loads. - (
@useis the modern replacement for the older@import, which Sass is phasing out.)
@extend — share a base style
@extend lets one selector inherit another’s rules, so related components share a base without repeating it:
%card-base { // a "placeholder" base, only used via @extend
border-radius: 12px; padding: 16px; background: #fff;
}
.product-card { @extend %card-base; border: 1px solid #e6e8f0; }
.alert-card { @extend %card-base; border: 1px solid #fca5a5; }The %card-base is a placeholder — a base set of rules that only appears in the output if something extends it. Both .product-card and .alert-card use @extend %card-base to inherit the shared radius, padding and background, then add their own border. This keeps the common look in one place. The compiled CSS groups the shared selectors together so the base declarations are written only once.
Tip: To actually compile Sass, the common tools are the Sass CLI (run sass styles.scss styles.css in a terminal) or a build tool like Vite, which compiles .scss automatically. You will set this up in the next lesson on build tooling, or your framework handles it for you.
Q. Why does Sass need a compile step before the browser can use it?
✍️ Practice
- Write an SCSS file with a
$brandvariable, a nested.cardrule, and compile it (or trace by hand) to the plain CSS it produces. - Create a
@mixinfor a button style that takes a colour argument, and@includeit for two different buttons.
🏠 Homework
- Refactor one project’s CSS into SCSS: pull colours into
$variables, nest each component, and split buttons and cards into partials combined with@use.