How to Use Sass/SCSS in WordPress Themes

Sass is a CSS preprocessor that adds variables, nesting, mixins, and partials to plain CSS. The SCSS syntax (a superset of CSS) is the most widely used variant — any valid CSS is valid SCSS. For WordPress themes, the standard workflow is to author SCSS source files and compile them to a single CSS file that you enqueue.

Problem: Writing plain CSS for a WordPress theme is repetitive — there are no variables, no nesting, and no way to split styles into logical partial files.

Solution: Use Sass/SCSS — install it as a dev dependency with npm, compile SCSS source files into a single minified CSS file, and enqueue the compiled output. Use filemtime() as the version argument for automatic cache busting.

Install the Sass compiler and set up npm scripts:

npm init -y
npm install --save-dev sass

# package.json scripts section:
# "sass": "sass src/scss/style.scss dist/css/style.css --style=compressed",
# "sass:watch": "sass src/scss/style.scss dist/css/style.css --watch"

A typical theme SCSS structure with partials:

src/scss/
├── style.scss          # main entry — imports everything
├── _variables.scss     # design tokens: colours, fonts, spacing
├── _reset.scss         # CSS reset / normalize
├── _typography.scss    # headings, body text
├── _layout.scss        # grid, containers
├── _header.scss
├── _footer.scss
├── _buttons.scss
└── _woocommerce.scss   # WooCommerce overrides

Example _variables.scss and usage:

// _variables.scss
$color-primary:   #3498db;
$color-text:      #333;
$font-base:       'Open Sans', sans-serif;
$spacing-unit:    1rem;
$bp-md:           768px;

// _buttons.scss
@use 'variables' as *;

.btn {
    background: $color-primary;
    color: #fff;
    padding: $spacing-unit * 0.5 $spacing-unit;
    font-family: $font-base;

    &:hover {
        background: darken( $color-primary, 10% );
    }

    &--outline {
        background: transparent;
        border: 2px solid $color-primary;
        color: $color-primary;
    }
}

@mixin respond-to( $bp ) {
    @media ( max-width: $bp ) { @content; }
}

.nav-menu {
    display: flex;

    @include respond-to( $bp-md ) {
        flex-direction: column;
    }
}

Enqueue the compiled CSS in functions.php:

add_action( 'wp_enqueue_scripts', function() {
    wp_enqueue_style(
        'mytheme-style',
        get_template_directory_uri() . '/dist/css/style.css',
        [],
        filemtime( get_template_directory() . '/dist/css/style.css' )
    );
} );

NOTE: Use filemtime() as the version argument instead of a hardcoded string — it generates a new cache-busting version number automatically whenever the compiled file changes, without requiring manual version bumps.