Learn how you can leverage Grunt and LibSass as groundwork to improve your Frontend Development process and deliver better optimized sites for clients.
At NEWMEDIA we’ve been utilizing Sass and Compass compiled with Ruby for CSS Preprocessing for quite some time now in our Drupal projects. While this toolset has served us well, the toolset for Frontend Developers has been growing and improving tremendously as time goes on. Thus, we want to leverage those tools not only to improve our day-to-day development tasks, but more importantly to better provide more optimized websites for our clients.
In this post we will walk through overhauling our Frontend tooling with Grunt and LibSass, what’s involved, and what the benefits are for clients. For the purpose of this article, we’re just going to focus on getting Grunt and LibSass up and running – from there the heavy lifting is done, and you’re free to expand on your Grunt tasks as you see fit.
What’s the client benefit?
Implementing these tools allows us to work faster and spend less time waiting for tools to run. The other key benefit is that, since Grunt runs arbitrary tasks, we can add more tools to ensure our end product is optimized – not just CSS, but also Javascript, images, and more, resulting in all-around faster-loading websites.
What are these Frontend tools & why are we using them?
Grunt is a task runner built in Javascript that leverages node.js. Currently, there are two popular task runner projects: Grunt and Gulp. While there are slight pros and cons between the two projects, we decided to implement Grunt for our Drupal projects, as our WordPress team had already begun to use Grunt, and we wanted to converge some of our tooling and processes.
LibSass is an implementation of the Sass compiler written in C instead of Ruby. The primary benefit over the original Ruby compiler is a tremendous boost in speed resulting in much faster compile times.
Note on LibSass: Unfortunately LibSass does not yet have feature parity with Ruby Sass, but it’s not too far off and continues to improve. This Sass Compatibility reference is handy for specific features that may be missing. Personally, I have yet to hit an issue with feature parity, but your mileage may vary.
Get the prerequisites in place
You will need node.js if you don’t have it already. I suggest using the “n” version management tool to help manage your node install: https://github.com/tj/n
Once you have n installed, install the current stable version of node:
n stable
Next, you’ll need to get a couple node modules installed globally: grunt-cli, the commandline interface for grunt, and bower, a dependency manager for web projects.
npm install -g grunt-cli
npm install -g bower
From here, many of the node modules we’ll need can be managed at a project level.
Setting up Grunt for your project
Now that we have our prerequisites in place, we need to set up three files within our project:
- package.json to manage our project-level node dependencies
- Gruntfile.js to define our tasks
- bower.json will be used to define our sass library dependencies (in our case, singularity and breakpoint).
Special note on Drupal projects: Currently, placing node_modules within Drupal’s directory can potentially cause it to crash, and otherwise could negatively impact performance. We structure our repository so that we have the Drupal directory contained in a folder called docroot, so we handle node at the root level of our project repo above the Drupal directory, rather than at the theme level. See these Drupal Core issues for more information:
- Ignore vendor folders to improve directory search performance
- Allow file_scan_directory() to ignore folder
package.json
Here’s our example package.json:
{
"name": "NEWMEDIA",
"version": "0.0.1",
"description": "NEWMEDIA grunt tools",
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-watch": "^0.6.1",
"grunt-sass": "^0.18.1",
"load-grunt-tasks": "^3.1.0"
}
}
The devDependencies section defines the node modules we’re using:
- grunt (Grunt itself is installed on a per-project basis, only the commandline interface grunt-cli is installed globally),
- “grunt-contrib-watch”, which provides a watch tool (think sass watch or compass watch),
- grunt-sass, which will bring in and manage libsass for us, and
- load-grunt-tasks, which allows us to automatically load our grunt dependencies in, rather than manually specifying each one in our Gruntfile.js
As times goes on, you’ll likely want to add dependencies to your toolset. Adding them with “npm install [name] –save-dev” will download the module for you and automatically add it to your package.json.
bower.json
Using the Ruby Sass compiler, you use Bundler to manage your Sass library dependencies. Bower will take the place of that in our Grunt/LibSass workflow:
{
"name": "NEWMEDIA",
"version": "0.0.1",
"description": "NEWMEDIA build dependencies",
"authors": [
"NEWMEDIA!"
],
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"devDependencies": {
"breakpoint-sass": "~2.5.0",
"singularity": "~1.6.2"
}
}
Gruntfile.js
Now that we have dependency management out of the way, we can setup our tasks. Here’s our example Gruntfile:
module.exports = function(grunt) {
// Load tasks automatically with 'load-grunt-tasks' plugin.
require('load-grunt-tasks')(grunt);
// Define our theme directory
var themeDir = 'path/to /theme';
// Specify where to find dependencies we load in with Bower
var sassLib = ['bower_components'];
// Define the CSS files we want compiled from SCSS files
var sassFiles = {};
sassFiles[themeDir + '/css/style.css'] = themeDir + '/scss/style.scss';
sassFiles[themeDir + '/css/print.css'] = themeDir + '/scss/print.scss';
// Project configuration.
grunt.initConfig({
sass: {
dev: {
options: {
sourceMap: true,
outputStyle: 'expanded',
includePaths: sassLib
},
files: sassFiles,
},
build: {
options: {
sourceMap: false,
outputStyle: 'compressed',
includePaths: sassLib
},
files: sassFiles,
}
},
watch: {
sass: {
files: ['**/*.scss'],
tasks: ['sass'],
}
},
});
// Default task(s).
grunt.registerTask('default', ['sass:build']);
grunt.registerTask('dev', ['sass:dev']);
grunt.registerTask('build', ['sass:build']);
};
Changes to your Sass
Now that we have the tools in place, we will need to update how we import our sass libraries. Fortunately this is a simple change. Here are our existing imports:
@import "breakpoint";
@import "singularitygs";
These imports get updated to:
@import 'breakpoint-sass/stylesheets/breakpoint';
@import 'singularity/stylesheets/singularitygs';
The exact change to your Sass imports may be different depending on the library. To determine the path you need to use, add the library to bower.json, and look inside bower_components to locate the library and determine the path to the main scss file to import.
Note on Compass
At the time of writing this post, Compass is still dependent on Ruby and can’t be used with LibSass. Depending on your case, there are a couple options. If you are using Compass mixins and want to continue to use them, there is a compass-mixins library you can use with LibSass. If you primarily use Compass to handle vendor prefixes, you might want to look at adding Autoprefixer to your Grunt task instead.. If you’re using other libraries that depend on Compass, it’s likely that even using the compass-mixins library, you won’t be able to use those libraries with LibSass.
Ready to Go!
At this point you should be in a good place to try running your grunt task. In our example, we have two defined tasks, build and dev, as well as our watch task. Try running “grunt dev” to do a one-time run. Once you are getting successful task runs, you are ready to start working on your project! You can now use “grunt watch” to detect changes and automatically run tasks.
You’re also now at a good point to look at adding additional tasks to your workflow. Some suggestions would be to add tasks to handle optimizing your Javascript and image assets. You can find an expansive selection of tasks on the official Grunt Website’s plugin directory.