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.