How we use Sass and Gulp in our WordPress theme & plugin development workflow

It’s been quite a while now since we’ve completely abandoned vanilla CSS for a CSS preprocessor (more than four years actually) and more specifically for Sass (with SCSS syntax). There are quite a few reasons why we did that, and the main one is improved DX (developer experience) along with easier plugin integrations.
Simply put, Sass used to be (and still is) a much more powerful language than vanilla CSS, especially if you’re concerned with older browser support (i.e… IE ¯\_(ツ)_/¯). I’ve found that the need for variables, conditionals, mixins, rule nesting, along with color functions and all the goodies a preprocessor comes with is much more apparent within the WordPress theming context where you wish to provide multiple color schemes for your theme or tame popular WordPress plugins to match your theme’s design and layout.
That said, I’m not here to sell you on CSS preprocessors right now, this piece is an overview on how we use Sass with Gulp in our theme and plugin development workflow, and how to incorporate the same workflow to your theme, if you so wish.
Our specific requirements
Back when we started exploring how we’d go about integrating Sass in our themes we had a few hard requirements that usually do not apply to other projects (like standalone apps or websites). This is because our customers range from simple website owners with little to no CSS knowledge to website developers with varying skillsets (not everyone is a front end developer). The above means that:
- All our themes must provide a complete vanilla CSS stylesheet (the main theme’s stylesheet – style.css) which mustn’t look like it was compiled from something else (to the best extend possible) and, more crucially, be readable. A lot of people simply want to add a few touches here and there or copy and paste selectors in their child theme’s stylesheet; they shouldn’t have to deal with Sass if they don’t want to.
- We do not ship themes with a minified stylesheet, for obvious reasons like mentioned above (also it’s not really allowed by the WordPress theme guidelines).
- All vendor stylesheets (e.g. from jQuery plugins) should be in their separate files and not included in the main theme’s stylesheet which must contain only theme styles.
- Sass should be transparent: structure our file hierarchy so that our users can work both with Sass or vanilla CSS depending on experience and preferences.
With the above in mind we’ve settled down in the following file structure with regards to stylesheets:
├── css (All CSS/Sass files except the main one are included here) │ ├── inc (Sass partials - theme styles only) | | ├── _base.scss | | ├── _header.scss | | ├── _footer.scss | | ├── _modules.scss | | ├── _woocommerce.scss | | └── ...etc │ ├── font-awesome.scss │ ├── font-awesome.css │ ├── magnific.scss │ ├── magnific.css │ ├── mmenu.scss | ├── mmenu.css │ └── ... other vendor stylesheets ├── fonts ├── images ├── js (All JavaScript files) ├── ... ├── ... ├── style.css (Compiled theme styles) └── style.scss
This allows us to keep Sass merely as an enhancement for the end user that prefers CSS, and still be prominent enough to be used if someone else so chooses.
All compiled files are right next to their sources and each one can easily be ignored in favor of the other. Also notice we’ve stuffed all Sass partials in the /css/inc
directory exactly so that they can be out of the way, if required.
The main style.scss file simply imports all partials, and contains no actual styles in it. For example:
Compiling Sass with Gulp
Gulp usually needs no introductions, but for the sake of completeness, it’s a task runner tool for any kind of task automation in development workflows, and it’s blazing fast at it. It can handle virtually any task with its powerful plugin architecture; from JavaScript or CSS compilation and minification to actual deployments or other more complex tasks. It is JavaScript based and runs on Node.js.
We use Gulp extensively at CSSIgniter. We have over a dozen different Gulp tasks in our internal toolkit for our various workflows (which we’ll cover in another tutorial), one of them being Sass compilation which we’ll glance over right away.
Getting started
All Gulp needs to run is to have Node.js installed. After that we’ll need to initialize a project with npm
. First create a directory where your project will live in with the structure we’ve mentioned in the previous section and initialize the project:
A package.json
file is immediately created for us, which is used to keep track various information about the project such as its dependencies.
For our Sass toolkit we’ll need to install the following dependencies:
Having done that, let’s also globally install Gulp on our system so that we can actually run it as a command:
Another note: If you don’t want to pollute your system with global dependencies (and you shouldn’t) you can run gulp
as an npm script in package.json
. Under "scripts"
add: "gulp": "gulp"
. Now running npm run gulp taskname
will be the same as running gulp taskname
.
After that we’ll need to create a file named gulpfile.js
. This will be our toolkit’s main file, as Gulp expects and reads this in the current working directory any time we run gulp
from the command line.
Inside gulpfile.js
, require our dependencies:
And our Sass compilation task:
This is very close to what we use for our themes. Now running gulp compile:sass
(or npm run gulp compile:sass
) in the command line at our project’s root will handle all our Sass compilation. Let’s go over the key features:
This is the key ingredient of our task, enabled by the gulp-sass module. It’s where Sass actually gets compiled to CSS. We properly indent and output in expanded style so that we meet our readability requirements.
Autoprefixer is such an amazing tool. As the name implies, it automatically adds browser/vendor prefixes to our code for just the browser versions we’ve specified (in this particular example it would be the last 2 versions of all browsers). This allows us to freely write CSS without ever having to remember to add prefixes by ourselves (e.g. on flexbox) while maintaining support for older browsers that require them.
One more benefit is that when we decide to drop support for a particular browser (or as browsers advance in versions), all we need to do is remove that browser from our gulpfile
, recompile the CSS, and all relevant prefixed statements will be gone!
Another God-sent tool: gulp-group-css-media-queries
. To understand why we need it let’s see how we usually write CSS Media Queries with Sass:
Nesting Media Queries under their related elements is very handy, it allows us to encapsulate every style under each selector. The problem with this approach is that it gets compiled into:
Which has a few problems. Mainly it creates a new Media Query block for each declaration, increasing filesize and impacting readability significantly.
With group-css-media-queries
we avoid all these issues. The plugin simply traverses our code and groups together all matching Media Queries at the end of our stylesheet, so that the output would become:
Efficient, and much cleaner!
Our final line, streams the results to browserSync, which we’ll cover right away, after we create a new task which will start watching our Sass files for any changes and immediately compile and stream them to the browser.
This task actually does a bit more: First it initializes a browserSync
server instance. BrowserSync is another amazing tool with a lot of uses.
In this particular scenario we use it as a proxy to our local WordPress development server (which in this case is under http://localhost/wordpress-installation
— replace this with your own local dev URL). That way we can automatically stream all file changes to browserSync
which will handle reloading the page for us, or in the case of CSS, it’ll directly and transparently inject the new styles without even reloading! A huge boost in productivity all around.
Then we simply watch all Sass files for changes with Gulp’s internal API (gulp.watch
), triggering the compile:sass
task (which then streams the changes to browserSync
, which then injects the styles).
And to conclude, adding a gulp default task:
Now any time we run just gulp
, the watch:sass
task will trigger since we’ve declared it as the default one.
Bonus: Linting our styles with sass-lint
No development workflow would be complete without a linter. Linting code has huge benefits, it prevents mistakes and helps maintain a concise and uniform codebase. Adding linting to the above workflow is as simple as creating a new task. Let’s first install the required dependency:
And append the lint task in our gulpfile.js
:
Notice that we’ve also made the new lint:sass
task running at various parts of our workflow. Now every time we either compile Sass files or watch for changes the linter will automatically trigger.
Note: You can use the sample sass-lint config file to create your own linting rules.
And the full gulpfile.js
:
And that’s about it! As an exercise to the user I’ll leave it up to you to add sourcemap support or other goodies such as more advanced PostCSS plugins!
Do you use a CSS preprocessor in your WordPress projects? How about Gulp? Let us know of your experiences, tips, or tricks in the comments below.
19 responses to “How we use Sass and Gulp in our WordPress theme & plugin development workflow”
Interesting insights Vassilis. Have you had a chance to check WPGulp? I built it about four years ago and have iterated on it a lot. 500+ devs use it.
Take a look: https://github.com/ahmadawais/WPGulp
Hey Ahmad! Thanks for your comment. I have to admit it’s the first time I have a look at WPGulp and it looks like a great tool. Our Gulp workflow is very similar although we do not minify styles and scripts since our products are extensible themes. Keep it up!
Never know about grouping media queries in scss, thats a god send. And to know there’s a plugin to bring them all together is amazing. Thank you for the write up!
Hi. Is there a way to create a task to upload modified files directly to the server? Maybe autenticating using ftp credentials. I have a staging wordpress site on my server, and would love to code my theme locally and with automate upload. Let me know your thoughts about it. Thanks
Hiya! There’s a gulp plugin for everything! :) In your case vinyl-ftp is what you’re looking for https://github.com/morris/vinyl-ftp
This was a helpful article. I did notice that gulp-sourcemaps is a required dependency in your gulpfile.js but it is not being used anywhere, nor was the package installed. I removed that line and had no issues. Is that module something you make use of in your normal workflow?
Ah yes, gulp-sourcemaps is an artifact indeed as it’s not really used in the examples. Source maps can be very useful during the development process and I’ve left it as an exercise for the reader as to how to enable them (the plugin’s repo has thorough and simple docs https://www.npmjs.com/package/gulp-sourcemaps)
How easy or challenging would it be to incorporate live reloading into this mix of this tutorial?
The tutorial already includes how to live reload with Browsersync (actually not even reload, streaming injections which are better), let me know if you mean something different.
Where should the init happen? Is this inside our WordPress Applications main directory? In my case, my child theme directory? Or some where else?
Thanks.
It can be anywhere as long as the paths to your stylesheets are correct, but I suggest from the main root folder of your theme :)
If someone were to directly edit a css file and later someone edited an scss file and compiled it, would that overwrite the earlier changes to the css file?
Exactly yes, it would override the change, so it’s important to only edit the source files (SCSS).
Hello Vassilis,
I try to use your gulpfile for a basic theme based on underscores.me with WooCommerce.
After installing the dependencies and running `gulp` I get the error “Task function must be specified”. What am I missing? I’m familiar with HTML and CSS, JS is still a mystery for me :(
Hello! Can you paste the contents of your gulpfile.js in a bin somewhere and put them here?
Hello, I think it’ the same you posted here: https://pastebin.com/2c1Jd49N
Thx!
I just had same problem. Either change your gulp version to 3.9.1 or change modify gulpfile.js with new gulp 4.0 syntax.
https://stackoverflow.com/questions/51098749/everytime-i-run-gulp-anything-i-get-a-assertion-error-task-function-must-be
I’m trying to use gulp for a custom WP plugin. The “install” command creates a new “node_modules” folder. Query: how do you deal with this in your own themes? Do you have a “node_modules” folder in your theme? Do you delete this prior to upload / shipping to clients? Or is there a way to place the folder outside the project folder?
Used to have the node_modules inside the theme and then have a “build” command that simply copied and zipped only the relevant theme files (i.e. everything excluding the gulp file and node_modules) for release.