LibSass, Grunt, and Node NPM: Do You Feel the Need For Speed?

LibSass, Grunt, and Node NPM: Do You Feel the Need For Speed?

In this article I'll show you how to start using LibSass which is a super fast rendering engine for Sass. If you're coming from a Ruby Sass environment you may be experiencing slow compile times with larger code bases. LibSass gets around these bottlenecks and compiles in milliseconds even for a site with lots of partials, complex mixins, and multiple libraries.

The first time I got LibSass running, I could not believe what I was seeing. I converted a Ruby Sass site over to LibSass and I went from 16 - 20 second compile times to an average of 150 milliseconds, that's 0.150 seconds. Essentially with LibSass, compiling is instantaneous. Add in LiveReload and you've got a real time super fast workflow. No more waiting for Sass compiling and manually reloading your browser or device for testing.

What's LibSass and Grunt?

Grunt is a JavaScript task runner that helps streamline your workflow and automate what are normally mundane tasks so you can concentrate on what counts, design and development of websites. Grunt does things like CSS and JS minification, autoprefixing, code compilation, and a whole host of other tasks.

LIbSass is a C/C++ port of the Sass engine so it's no longer reliant on Ruby. For our setup, we'll use Grunt Sass which is an off-shoot of Node Sass which in turn is a library that binds to LibSass. If that sounds a little confusing, it is but I'll help sort through all that.

Grunt Sass is not to be confused with Grunt Contrib Sass which is the Ruby version of Sass for Grunt. For Grunt Sass, we have no dependency on Ruby or Compass. For our setup, we'll use the popular node.js NPM or Node Package Manager. It's a great way to start using Grunt and installing LIbSass and other libraries in short order. If you don't know anything about node.js, don't worry, we're only installing it to use NPM so little to no Node knowledge is necessary here.

I was initially confused by all these new names and terms, but I found that by just searching around npmjs.com, I would find some really cool modules to use. Each has their respective links to Github projects with in depth information and how-to's.

Getting Started: Node and package.json

My setup is on OS X Yosemite, 10.10.1 but this should work on other OSes, milage may vary. The first thing to do is to be sure Node is installed. Type node -v in terminal. If it's installed, you should get something back like v0.10.35. If not, then head over to nodejs.org, click the big green install button, download and install. There's installers for Windows, Mac, and Binaries for Linux.

Once you have Node installed, start a local site. The first thing to do at the root of your site (or theme in the case of a CMS) is to create a package.json file. This is a representation of the Node modules you want to use and it tells Node what to install. This is what my basic file looks like:

{
  "name": "LibSass-Theme",
  "version": "0.0.1",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-handlebars": "~0.7.0",
    "grunt-contrib-uglify": "~0.5.1",
    "grunt-contrib-watch": "~0.6.1",
    "grunt-sass": "^0.17.0",
    "grunt-shell": "~0.7.0",
    "grunt-timer": "^0.5.8",
    "load-grunt-tasks": "~0.2.1",
    "node-bourbon": "^1.2",
    "susy": "^2.2.2-alpha.1"
  }
}

Knowing what modules to use and what to put in package.json takes a little trial and error, and some research. In the end, the above ends up working well.

You might ask, "how do you know what Node module versions to use?" The best way to is to take a look at the Node module page and see what the latest version is. For example if I go to node-bourbon on https://www.npmjs.com, I see the current version is 1.2.3. Because I have specified the ^1.2 version using the caret, when I go to install, 1.2.3 will install. From a Stackoverflow post, this is explained in more detail:

In the simplest terms, the tilde matches the most recent minor version (the middle number). ~1.2.3 will match all 1.2.x versions but will miss 1.3.0. The caret, on the other hand, is more relaxed. It will update you to the most recent major version (the first number). ^1.2.3 will match any 1.x.x release including 1.3.0, but will hold off on 2.0.0.

Once you have your package.jason file configured, in Terminal, you can cd to the root where that file is and then run npm install. This command will install all the specified Node modules. You can also add new modules that are not already in package.json from the command line simply by suffixing the command with --save-dev. So let's say I find a module I want to use from Node NPM called grunt-concurrent, I would run:

npm install --save-dev grunt-concurrent

That will now add a new entry in package.jason called "grunt-concurrent": "^1.0.0"

One thing to note that got me at the beginning was that I had a space in my package.json "name" tag. This will throw some vague errors so best to have a dash or underscore, in my example above, "name": "LibSass-Theme".

Configure your gruntfile.js

Now that we have our Node modules installed, we can create and configure our gruntfile.js. This is the main file that tells grunt what to do and where to do it. Again this file lives at the root of your site or theme depending on your configuration. Once you have this set, your directory structure should look something like this:

|____css
| |_____(compiled css here...)
|____gruntfile.js
|____index.html
|____node_modules
| |____grunt
| |____grunt-contrib-watch
| |____grunt-sass
| |____node-bourbon
| |____susy
| |____(more modules here...)
|____package.json
|____sass
| |____styles.scss
| |____normalize.scss
| |___ (more partials here...)

gruntfile.js can be fairly large and complex in nature. It's used to define and configure tasks and load your defined Grunt plugins specified in package.json. It's typically structured like this:

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),
        { // Tasks here. } 
       });
      };

There will typically be a watch {} task within which looks for any changed Sass, JS, or HTML files. You can specify which types of files get watched and where they get watched.

watch: {
      options: {
        livereload: true
      },
      site: {
        files: ['templates/**/*.tpl.php', 'js/**/*.{js,json}', 'css/*.css', 'images/**/*.{png,jpg,jpeg,gif,webp,svg}']
      },
      css: {
        files: ["source/sass/**/*.scss"],
        tasks: ["sass"]
      },
    } // watch

In addition to a watch: {} task, gruntfile.js can have a sass: {} task. The watch task handles changes with files and what files to watch so that LiveReload can react and update the page you are working on in realtime. As you can see there are some paths and wildcards setup according to our site architecture and Sass structure. You can also implement LiveReload so that you never have to refresh your browser to see changes. This, in combination with LibSass essentially gives a developer instant code changes visually on the rendered web page and streamlines development immensely. I don't use any browser or software plugins for LiveReload, I've found that simply placing the livereload.js in a template or html page works nicely.

// Livereload for localhost.
<script src="http://localhost:35729/livereload.js?nioytj"></script>

gruntjs.com has a really nice sample gruntfile.js with explanations on their site but you can also take a look at the file in my Gist for this article linked in the resources below.

The grunt sass: {} task does a few things here; it lets Grunt know what CSS files correspond to our SCSS files. That way, at compile time, grunt knows where to put the rendered CSS. The task also tells grunt in what manner to output the CSS:

sass: {
      global: {
        options: {
          sourceMap: true,
          sourceComments: false,
          outputStyle: 'expanded'
        },
        files: [{
          expand: true,
          cwd: '<%= paths.sass %>/',
          src: ['**/*.scss'],
          dest: '<%= paths.devCSS %>/',
          ext: '.css'
        },
        ],
      }
    }, // sass

Here we tell Grunt to use source maps, leave out comments and give us expanded CSS syntax. We could always run an "uglify" task as well to minify our code, yep there's a Grunt plugin for that. Source maps are great for browser code debugging as it tells you what SCSS partials the code comes from and what lines are involved. Ideally, if you are using Chrome, you can enable CSS source maps in the preferences so that you can easily theme and debug your SCSS files.

Finally, we register a default grunt task so that we can simply run grunt in Terminal rather than grunt watch.

require("load-grunt-tasks")(grunt);
  grunt.registerTask("default", ["sass", "watch"]);

We can see here that our "default" task registers both the watch and sass render functions. In my travels, I found that no two grunt files were ever alike for similar examples so it does get a bit confusing at times. It turns out that there seem to be many different ways you can lay out a grunt file to achieve similar tasks.

Conclusion

I'm so happy I've invested an immense amount of time over the past few months getting LibSass running for my front-end workflow. During this time, I've also seen LibSass grow immensely to a point where it's getting close to feature parity with Sass. I can't ever imagine going back to Ruby Sass. For further reading you can have a look at the Sass Compatibility chart by @HugoGiraudel. I also found a nice Grunt tutorial on Lynda.com which was instrumental in helping me get up to speed with the basics of Grunt. You can also take a look at my Gist which contains a sample gruntfile.js and package.json. This is by no means definitive or perhaps even the best way to do things but it works really well for me in a variety of environments.

Related Tweets

Resources

Tags