CSS Preprocessors: Sass and PostCSS
Preprocessors extend CSS with programming features — variables, nesting, mixins, loops, and more — then compile to standard CSS that browsers understand.
1. Sass vs SCSS
Sass has two syntaxes:
- SASS (
.sass): Indentation-based, no curly braces. Less common today. - SCSS (
.scss): CSS superset — all valid CSS is also valid SCSS. Use this.
// SCSS — looks just like CSS + superpowers
.card {
background: #1c1c1e;
border-radius: 8px;
padding: 2rem;
&:hover { transform: translateY(-4px); } // Nesting with parent reference
&__title { font-size: 1.5rem; } // BEM — card__title
&__body { color: #888; }
}
2. Variables
Sass variables use $. They are resolved at compile time (unlike CSS variables which are runtime):
$color-primary: #6c63ff;
$font-heading: 'Playfair Display', serif;
$radius-md: 8px;
$shadow-md: 0 4px 12px rgba(0,0,0,0.2);
.button {
background: $color-primary;
border-radius: $radius-md;
box-shadow: $shadow-md;
}
[!TIP] In modern projects, prefer CSS Custom Properties (
:root { --var: value }) for runtime-changeable values (theme toggling, JS updates), and Sass variables for compile-time constants (file structure, breakpoints).
3. Nesting
Nest selectors to mirror your HTML structure and keep related code together:
.nav {
display: flex;
gap: 2rem;
&__item { list-style: none; }
&__link {
opacity: 0.8;
transition: opacity 0.2s;
&:hover { opacity: 1; }
&.active { opacity: 1; font-weight: 700; }
&::after { content: ''; /* underline decoration */ }
}
}
[!WARNING] Don't nest more than 3 levels deep. Deep nesting creates overly specific selectors that are hard to override.
4. Mixins
Reusable blocks of CSS declarations. The workhorse of Sass:
// Define
@mixin flex-center($direction: row) {
display: flex;
flex-direction: $direction;
align-items: center;
justify-content: center;
}
@mixin respond-to($breakpoint) {
@if $breakpoint == 'md' { @media (min-width: 768px) { @content; } }
@if $breakpoint == 'lg' { @media (min-width: 1024px) { @content; } }
@if $breakpoint == 'xl' { @media (min-width: 1280px) { @content; } }
}
// Use
.hero { @include flex-center(column); min-height: 100vh; }
.container { max-width: 1200px; @include respond-to('md') { padding: 0 2rem; } }
5. Functions and Math
// Built-in math
$base-size: 16px;
.heading { font-size: $base-size * 2; } // 32px
// Color functions
$primary: #6c63ff;
.hover { background: darken($primary, 10%); }
.muted { background: lighten($primary, 30%); }
.faded { background: rgba($primary, 0.2); }
// Custom functions
@function rem($pixels) {
@return $pixels / 16px * 1rem;
}
h1 { font-size: rem(48px); } // → 3rem
6. Partials and Modules (@use)
Split CSS into organized files — each focused on one concern:
styles/
_tokens.scss // Design tokens ($colors, $spacing)
_reset.scss // CSS reset
_typography.scss // Heading/body base styles
_layout.scss // Grid/container utilities
components/
_buttons.scss
_cards.scss
_forms.scss
main.scss // Imports everything
// main.scss
@use 'tokens';
@use 'reset';
@use 'typography';
@use 'layout';
@use 'components/buttons';
@use 'components/cards';
7. @extend — Use Sparingly
@extend shares rules between selectors:
%button-base {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
}
.btn-primary { @extend %button-base; background: $color-primary; }
.btn-ghost { @extend %button-base; background: transparent; }
[!WARNING] Prefer mixins over
@extend. Extend creates unexpected selector chains in the output CSS that can be hard to debug.
8. Installing and Running Sass
# Via npm (recommended for WordPress child themes)
npm install -D sass
# Compile once
npx sass styles/main.scss dist/style.css
# Watch mode (recompiles on save)
npx sass --watch styles/main.scss:dist/style.css
# With compression for production
npx sass --style=compressed styles/main.scss dist/style.min.css
9. PostCSS: The Modern Alternative
PostCSS is a CSS transformation tool that runs plugins. It can replace many Sass features:
| Sass Feature | PostCSS Plugin |
|---|---|
| Nesting | postcss-nesting (now native CSS!) |
| Autoprefixes | autoprefixer |
| Minification | cssnano |
| PurgeCSS | @fullhuman/postcss-purgecss |
| Variables | Already native CSS |
PostCSS is the right choice for WordPress plugin/theme builds (WP-Scripts uses it internally).
10. Should You Use Sass in WordPress?
| Scenario | Recommendation |
|---|---|
| Small site, few pages | Native CSS + CSS variables — no build step needed |
| Medium site, child theme | Sass for organization, npm run build to compile |
| Large site, team build | Sass + PostCSS + WP-Scripts pipeline |
| Bricks Builder | Native CSS variables in Global Styles — no Sass needed |
| GP child theme + many pages | Sass partials for maintainability |