Skip to main content

🎨 Sass/SCSS Introduction and Setup

Plain CSS gets repetitive fast on real projects. Sass is a preprocessor that adds variables, nesting, and logic to CSS — then compiles down to the ordinary stylesheet the browser already understands. This lesson gets you from "what is it?" to a compiler running on your machine.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain what a CSS preprocessor is and the problems Sass solves
  • Distinguish the two syntaxes — indented Sass vs SCSS — and choose SCSS with confidence
  • Describe the compilation pipeline that turns .scss into browser-ready .css
  • Install Dart Sass and compile files in watch mode using several workflows
  • Lay out a modular partial-based folder structure for a Sass project

Estimated Time: 25–35 minutes  •  Difficulty: Beginner

Hands-on: Install Sass, compile your first SCSS file, and run the compiler in watch mode.

In This Lesson

What Is Sass?

SassSyntactically Awesome Style Sheets — is a CSS preprocessor. That means it is a language that looks like CSS but adds real programming features on top: variables, nesting, reusable blocks called mixins, functions, math, and even loops and conditionals. You write Sass, a tool compiles it, and out comes ordinary CSS that any browser can render.

The key idea is that browsers never see your Sass. They only ever load the compiled .css file. Sass is a tool for authoring stylesheets, not a new thing browsers had to learn.

📖 Key Terms

Preprocessor: a tool that transforms source you write into a different output format before it is used — here, Sass source into CSS.

Compilation: the step that runs the preprocessor and produces the final CSS file.

Dart Sass: the official, actively maintained Sass implementation. The old Ruby and Node-Sass engines are deprecated — always use Dart Sass today.

💡 Analogy — Sass is a kitchen appliance. Think of Sass as a food processor. Raw ingredients (your Sass code) go in, and a finished dish (CSS) comes out. Your dinner guests — the browser — only ever taste the final dish. They never see the appliance or the technique behind it.

Two Syntaxes: Sass vs SCSS

Confusingly, "Sass" names both the project and one of its two syntaxes. Here is the distinction:

  • The indented syntax (.sass) — the original. It drops braces and semicolons and relies on indentation, like Python.
  • SCSS (.scss) — "Sassy CSS". It uses the exact braces-and-semicolons syntax of regular CSS, and every valid CSS file is already valid SCSS.

The same navigation styles, written both ways:

Indented Sass syntax

nav
  ul
    margin: 0
    padding: 0
    list-style: none

  li
    display: inline-block

  a
    display: block
    padding: 6px 12px
    text-decoration: none

SCSS syntax

nav {
  ul {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  li {
    display: inline-block;
  }

  a {
    display: block;
    padding: 6px 12px;
    text-decoration: none;
  }
}

✅ Which should you learn?

Use SCSS. Because it is a superset of CSS, you can paste existing CSS in and improve it gradually, autocomplete works better, and nearly every framework and tutorial you will meet (Bootstrap, Bulma, design systems) ships SCSS. This entire module uses SCSS.

Why Use a Preprocessor?

Plain CSS is wonderfully simple, but on a large codebase its simplicity turns into pain: the same hex color copied into forty places, deeply repeated selector prefixes, no way to do arithmetic, and no real way to package a reusable chunk of styling. Sass targets exactly those gaps.

flowchart TB A[Plain CSS pain points] --> C1[Repeated values] A --> C2[Verbose selectors] A --> C3[No math or logic] A --> C4[Hard to modularize] B[Sass features] --> F1[Variables] B --> F2[Nesting & partials] B --> F3[Functions & operators] B --> F4[Mixins & @extend] F1 -.solves.-> C1 F2 -.solves.-> C2 F3 -.solves.-> C3 F4 -.solves.-> C4
FeatureWhat it gives youReal-world use
VariablesStore a color, font, or size once and reuse itOne brand palette shared across an entire e-commerce site
NestingMirror your HTML structure; write lessComponent-scoped styles in a design system
Partials & modulesSplit styles into small importable filesTheme systems for multi-brand companies
MixinsReusable, parameterized blocks of declarationsConsistent button and card styling everywhere
FunctionsCompute and return valuesDynamic spacing and type scales
Loops & conditionalsGenerate CSS programmaticallyUtility-class generators, à la Tailwind

⚠️ A note on CSS custom properties

Modern CSS has its own --variables, calc(), and even nesting. So is Sass obsolete? No — but the lines have blurred. Sass variables and math run at build time and disappear from the output; CSS custom properties live at runtime and can change with the cascade or JavaScript. Mature projects often use both: Sass for authoring conveniences (mixins, loops, partials) and CSS custom properties for runtime theming like light/dark mode.

How Compilation Works

Because browsers cannot read Sass, a compiler stands between your source and the browser. You edit .scss; the compiler watches for changes and regenerates .css; the browser loads that CSS.

The Sass compilation pipeline SCSS source files are read by the Sass compiler, which outputs a standard CSS file that the browser then renders. You write style.scss Sass compiler dart-sass (watch) Browser reads style.css compile link
Figure 1 — The compile step happens on your machine (or in your build server), never in the browser. In watch mode it re-runs automatically every time you save.

Your HTML always links the compiled file, exactly as it would with hand-written CSS:

<link rel="stylesheet" href="dist/css/style.css">

Installing & Running Sass

There are several ways to get a compiler running. Pick the one that fits how you already work.

Method 1 — Global install (fastest to start)

If you have Node.js installed, add the official Dart Sass command-line tool globally:

npm install -g sass

Compile a file once:

sass input.scss output.css

Recompile automatically on every save with watch mode:

sass --watch input.scss output.css

Watch a whole folder, mapping a source directory to an output directory:

sass --watch scss:css

Method 2 — Project dependency (recommended for real projects)

Keeping Sass local to the project means every teammate compiles with the same version. Add it as a dev dependency:

npm install sass --save-dev

Then wire up scripts in package.json:

{
  "scripts": {
    "sass": "sass src/scss:dist/css --watch",
    "sass:build": "sass src/scss:dist/css --style=compressed --no-source-map"
  }
}

Run the watcher while developing, or the minified build for production:

npm run sass          # develop with live recompile
npm run sass:build    # one-off compressed output

Method 3 — Build-tool integration

Most modern bundlers understand SCSS with almost no configuration:

  • Vite — just install sass and import a .scss file; no config needed.
  • Webpack — add the loader chain: npm install sass-loader sass css-loader style-loader --save-dev.
  • Next.js / Astro / SvelteKit — install sass and SCSS support turns on automatically.

Method 4 — VS Code extension (no terminal)

For quick learning without touching the command line, install the Live Sass Compiler extension. A "Watch Sass" button appears in the status bar; click it and it compiles on save.

What compiled output looks like

Given this SCSS:

$brand: #3b82f6;
.btn { color: $brand; }

the compiler emits plain CSS — the variable is gone, resolved at build time:

.btn { color: #3b82f6; }

A Modular Project Structure

The real payoff of Sass is splitting one giant stylesheet into small, focused partials. A partial is any file whose name starts with an underscore (_variables.scss). The leading underscore tells the compiler "don't build this into its own CSS file — it is only meant to be pulled into another file."

project/
├── src/
│   ├── scss/
│   │   ├── main.scss          # entry point — pulls in every partial
│   │   ├── _variables.scss    # colors, fonts, spacing tokens
│   │   ├── _base.scss         # resets and base element styles
│   │   ├── _layout.scss       # header, footer, grid
│   │   ├── _components.scss    # buttons, cards, forms
│   │   └── _utilities.scss    # helper classes
│   └── index.html
└── dist/
    └── css/
        └── main.css           # the one compiled output

Only main.scss lacks an underscore, so it is the only file the compiler emits. Inside it, you pull the partials together. Modern Sass uses @use for this (we cover it fully in the next lessons); the older @import rule still works but is now deprecated:

// main.scss — modern loading with @use
@use 'variables';
@use 'base';
@use 'layout';
@use 'components';
@use 'utilities';

💡 Why the underscore convention matters

Without it, a folder of ten partials would produce ten stray CSS files. The underscore keeps your output to a single, clean stylesheet while your source stays neatly organized.

Hands-on Exercise

🏋️ Compile Your First SCSS File

Objective: Install Sass, write a tiny SCSS file that uses a variable and nesting, and watch it compile to CSS.

Instructions:

  1. Create a folder sass-lab with subfolders scss/ and css/.
  2. Install Sass globally: npm install -g sass.
  3. In scss/main.scss, write a variable and one nested rule (a nav with a nested a).
  4. Run sass --watch scss:css and confirm css/main.css appears.
  5. Change the variable's value, save, and watch the CSS regenerate on its own.
💡 Hint

Define a variable at the top with the $name: value; syntax, then reference it inside a rule. Nest a child selector by placing its braces inside the parent's braces. Leave the --watch command running in its own terminal tab.

✅ Example solution

scss/main.scss:

$link-color: #3b82f6;

nav {
  background: #0f172a;

  a {
    color: $link-color;
    text-decoration: none;

    &:hover {
      text-decoration: underline;
    }
  }
}

After running sass --watch scss:css, css/main.css contains:

nav { background: #0f172a; }
nav a { color: #3b82f6; text-decoration: none; }
nav a:hover { text-decoration: underline; }

Notice how the nested rules flattened into standard CSS selectors and the variable resolved to its hex value.

Best Practices

✅ Do

  • Use Dart Sass and the SCSS syntax — they are the current, supported standard.
  • Keep partials small and single-purpose; assemble them in one entry file.
  • Run the compiler in watch mode while developing so feedback is instant.
  • Commit your .scss source and either build CSS in your pipeline or commit the compiled output — be consistent about which.
  • Ship --style=compressed CSS to production.

⚠️ Don't

  • Don't install node-sass — it is deprecated and unmaintained.
  • Don't hand-edit the compiled .css file; your next compile will overwrite it.
  • Don't reach for Sass just to declare a couple of variables — for that, plain CSS custom properties may be enough.

🎯 Quick Quiz

Question 1: Why can't a browser load a .scss file directly?

Question 2: What does the leading underscore in _variables.scss tell the Sass compiler?

Question 3: Which syntax should most new projects use, and why?

Summary & Quiz

🎉 Key Takeaways

  • Sass is a CSS preprocessor that adds variables, nesting, mixins, functions, and logic to CSS.
  • SCSS is the preferred syntax — a superset of CSS that every framework uses.
  • Browsers never read Sass; a compiler turns .scss into .css, ideally in watch mode.
  • Use Dart Sass, installed globally, per-project, or through a bundler.
  • Partials (underscore-prefixed files) let you split styles into small modules that assemble into one output.

📚 Further Reading

🚀 What's Next?

Now that Sass compiles on your machine, the next lesson digs into the three features you'll reach for every single day: variables, nesting, and partials. You'll learn variable scope, the powerful & parent selector, and how to architect a project with the 7-1 pattern.

🎉 Great start!

You have a working Sass compiler and a mental model for how it fits into your workflow. Time to start writing real SCSS.