Skip to main content

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 FeaturePostCSS Plugin
Nestingpostcss-nesting (now native CSS!)
Autoprefixesautoprefixer
Minificationcssnano
PurgeCSS@fullhuman/postcss-purgecss
VariablesAlready native CSS

PostCSS is the right choice for WordPress plugin/theme builds (WP-Scripts uses it internally).

10. Should You Use Sass in WordPress?

ScenarioRecommendation
Small site, few pagesNative CSS + CSS variables — no build step needed
Medium site, child themeSass for organization, npm run build to compile
Large site, team buildSass + PostCSS + WP-Scripts pipeline
Bricks BuilderNative CSS variables in Global Styles — no Sass needed
GP child theme + many pagesSass partials for maintainability