SCSS#
Sass is a CSS preprocessor; SCSS is its CSS-superset syntax. SCSS files compile to plain CSS, adding authoring conveniences, variables (since predating CSS variables), nesting, mixins, modules, and functions.
In 2026, native CSS has caught up on many of these features (custom
properties, nesting, color-mix, @layer), but SCSS still earns its
place in many codebases for compile-time logic and module composition.
Setup#
The minimal setup to compile SCSS: install sass, point it at
the source, watch for changes. Modern build tooling (Vite,
Next.js, Astro) handles this automatically once the sass
package is present.
$ npm install -D sass
$ npx sass src/styles.scss dist/styles.css --watch
Or via build tooling.
Vite, Astro, Next.js, etc. compile
.scssnatively when sass is installed.Stylelint with
stylelint-scssfor linting.
File Naming#
The convention SCSS uses to distinguish files compiled
directly from those imported into other files. Underscore
partials (_buttons.scss) are imported via @use;
non-underscore files are compiled to .css standalone.
styles.scss, entry; compiled to.css._buttons.scss, partial; not compiled on its own. Underscore prefix signals “include via@use”.
Variables#
SCSS variables are compile-time; the literal values get inlined into the output. For runtime theming you still want CSS custom properties; SCSS variables shine for compile-time calculations and design-system constants.
$color-bg: #fff;
$color-fg: #111;
$space-base: 1rem;
$radius-md: 8px;
.card {
background: $color-bg;
padding: $space-base * 1.5;
border-radius: $radius-md;
}
SCSS variables are compile-time. They don’t exist at runtime; the CSS output has the literal values. For runtime theming, use CSS custom properties.
:root {
--color-bg: #{$color-bg};
}
Nesting#
Authoring affordance for grouping related rules together. The output is plain flat CSS; the source structure stays readable even when selectors get specific. Native CSS nesting now exists too, and SCSS nesting compiles to similar output.
.card {
padding: 1rem;
&.is-featured {
border: 2px solid var(--accent);
}
.title {
font-weight: 600;
}
&:hover .title {
color: var(--accent);
}
}
&is the parent selector.Native CSS nesting now exists; SCSS nesting compiles to similar output.
Don’t nest deeply, selectors get specific and brittle. Two levels is usually plenty.
Mixins#
Reusable blocks of declarations, the SCSS equivalent of a function that emits CSS. Mixins handle the things that don’t generalize cleanly into custom properties: media queries, focus rings, scrollbar styling, complex hover behaviors.
@mixin card($padding: 1rem) {
padding: $padding;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,.1);
}
.product { @include card; }
.modal { @include card($padding: 2rem); }
Common in design systems for media queries, focus rings, scrollbar styling, etc.
Functions#
Built-in and user-defined. SCSS ships modules for math, color, string, list, map, and meta operations; the standard functions cover most authoring needs without writing your own.
@use "sass:color";
@use "sass:math";
$accent: #0b3d91;
.button {
background: $accent;
border-color: color.adjust($accent, $lightness: -10%);
padding: math.div(16, 14) * 1em;
}
@function strip-unit($value) {
@return math.div($value, ($value * 0 + 1));
}
The sass:math, sass:color, sass:string, sass:list,
sass:map, and sass:meta modules cover most needs.
Modules: @use and @forward#
The modern module system, replacing @import. Each
@use loads a partial once and exposes its members under a
namespace; @forward re-exports for assembly into a public
API. @import is deprecated; new code should use
@use / @forward:
// _tokens.scss
$color-bg: white;
$color-fg: #111;
// _components.scss
@use "tokens";
.panel {
background: tokens.$color-bg;
color: tokens.$color-fg;
}
// styles.scss
@use "tokens" as t;
@use "components";
body { background: t.$color-bg; }
@use, import a partial under a namespace; load once.@forward, re-export from a partial.Members starting with
_or-are private to their module.
@import is deprecated; new code should use @use / @forward.
Maps and Loops#
The compile-time data structures and iteration that justify
SCSS over plain CSS. @each and @for generate
families of utility classes from a token map, the pattern
that powers most design-system implementations.
$sizes: (
"sm": 0.875rem,
"md": 1rem,
"lg": 1.25rem,
);
@each $name, $value in $sizes {
.text-#{$name} { font-size: $value; }
}
@for $i from 1 through 5 {
.col-#{$i} { width: percentage(math.div($i, 5)); }
}
Inheritance: @extend#
A way to share declarations across selectors by extending
them at compile time; the compiler merges the using
selectors into one rule. Powerful but unpredictable; most
teams prefer @include over @extend because the
compiled output is easier to reason about.
%card-base {
padding: 1rem;
border-radius: 8px;
}
.product { @extend %card-base; border: 1px solid #ddd; }
.modal { @extend %card-base; box-shadow: ...; }
Use placeholder selectors (%name) with @extend to avoid surprising
selector lists in the compiled output. Most teams prefer @include over
@extend for predictability.
Nesting Media Queries#
Media queries can nest inside selectors in SCSS, which keeps responsive variants of a component co-located with the rest of its style. The compiled output is the same as hand-written non-nested CSS; the source structure is the authoring win.
.nav {
display: flex;
@media (max-width: 48rem) {
flex-direction: column;
}
}
SCSS versus plain CSS#
In 2026, native CSS has caught up on much of what SCSS used
to be needed for, including nesting, custom properties, color-mix,
@layer. The case for and against:
You like
@use/@forwardfor module composition.You want compile-time logic (loops, maps, mixins) for design tokens.
You inherit a SCSS codebase and don’t have a reason to migrate.
The case against SCSS, in greenfield work.
Native CSS nesting exists.
CSS custom properties handle most variable use cases at runtime.
color-mixandoklchcover a lot of whatcolor.adjustdid.Build-time CSS is one less thing to manage.
Pick by team and codebase. Both are professional choices.
Sass Syntax (the Indented One)#
A historical footnote. The original Sass syntax is
whitespace-significant and lacks braces or semicolons; it
still compiles (.sass files), but .scss (the CSS-
superset variant) is what modern codebases use, and what every
mainstream tutorial assumes.