Tania Rascia Web Design and Development

Skip Navigation
Getting Started with Grunt and Sass

Getting Started with Grunt and Sass

/ 24 responses

You’ve probably heard of Grunt, the JavaScript task runner that runs on Node.js. Maybe you’ve tried to get it up and running, but got lost or confused during setup. Maybe you know of it, but aren’t sure how it can be useful or if it’s worth the time invested. I’m going to show you how Grunt can be useful, and I’m not going to throw command lines at you and assume what you might already have installed. I’ll let you know exactly what you need to install, and in what order, to start optimizing your workflow.

Prerequisities

  • Ability to make a website with HTML, CSS, and JavaScript.
  • A very basic knowledge of the command line.

If you think you can follow along without reading that, here are the most important commands, and all you need to know to get started.

  • pwd Print Working Directory – shows the exact directory you’re working in.
  • ls List Directories – lists all the files and folders in your current directory.
  • cd Change Directory – change to another directory.

Goals

  • Understand enough about Node.js to get Grunt up and running
  • Create a Sass project
  • Utilize Grunt to compile multiple Sass files into one CSS file, apply prefixes, and minify.
  • Minify your JavaScript.
  • Run one command that will watch your entire directory for changes

What is a task runner?

Grunt and Gulp are currently the two most popular JavaScript task runners. The purpose of a task runner is to automate all your processes so you don’t have to think about them.

For us, this is going to be compiling our CSS preprocessor – Sass in this case, but the steps are the same for LESS – apply vendor prefixes for cross-browser compatibility, and minify the CSS for speed. We’ll also be minifying JavaScript.

What is Node.js?

JavaScript is, and has always been, a client-side programming language, which means it’s processed by the browser. With the advent of Node.js, JavaScript can also be used as a server-side language. Grunt runs in this environment.

How about npm?

Ah, npm – Node Pacakage Manager. Don’t let this be a source of confusion for you. Npm is just the package manager for Node.js, which just means it’s the tool to connect to the giant repository containing all the Node.js programs, plugins, modules and so on. Use the npm command to install everything that runs on Node.js.

The most important thing to learn about npm is the difference between local and global installs. Understand right now that you can’t do everything globally with npm! I cannot stress this enough. It was the basis of all my confusion when I was learning.

Local vs. Global

There has to be a local and global version of npm – it’s just the way it is. Don’t fight it. What does that mean? It means we’re going to install Grunt CLI globally – so I can run grunt in the command line at any time – and all the plugins and packages will go into local installs. If I’m working on mypersonalblog.com and clientwebsite.com, they both need their own npm install. I’m sorry. This is not a simple concept to understand at first. I certainly didn’t.

The command for local npm is npm, and the command for global npm is npm -g. Anything we do with -g affects your whole computer, and any regular npm command will only affect the current working directory.

Installation

Here are all the steps to get everything up and running.

1. Install Ruby

Sass runs on Ruby, so we need to have that installed. The Ruby programming language is already preinstalled on OSX. You can check this by opening Terminal and typing ruby -v to check the Ruby version number you have installed.

ruby -v
Ruby 2.0.0p645 (2015-04-13 revision 50299) [universal.x86_64-darwin15]

If you use a lot of Ruby, RVM (Ruby Version Manager) is recommended. I don’t, so I don’t care. You probably don’t need to, either.

2. Install Sass

You can also run sass -v to see if you have Sass installed already. If not, install it now.

sudo gem install sass
Fetching: sass-3.4.19.gem (100%) Successfully installed sass-3.4.19 Parsing documentation for sass-3.4.19 Installing ri documentation for sass-3.4.19 1 gem installed

3. Install XCode

XCode is a free download from the app store. You can check if you have XCode installed already by typing make in the Terminal. After you download it, install Command Line Tools: Preferences > Downloads > Command Line Tools. This is necessary for the next step. (Note: This step is no longer necessary as of 2017 – Homebrew will install XCode Command Line Tools for you.)

4. Install Homebrew

Homebrew is a useful program for installing other programs. So meta. Type this into the command line.

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Follow the steps – you’ll have to agree to installing brew and enter your password. You can type brew doctor to verify that Homebrew has been successfully installed.

5. Install Node.js

We’re going to use Homebrew to install Node.js.

brew install node

6. Install Grunt CLI globally

Finally, we can install Grunt. We’re not actually installing Grunt, but the Grunt command line interface (CLI). This basically just allows your computer to recognize the grunt command at all times. However, without a local install, you can’t actually use Grunt.

npm install -g grunt-cli

Here’s the good news: everything you just did, you won’t have to do again. Node.js is installed, Homebrew, XCode, Sass, Ruby. Maybe you’ll have to update them every now and then, but the point is that you have them. This is going to help with a lot of installations in the future.

Everything from here on out is going to have to be done on a project-by-project basis.

7. Create package.json in a local directory

Alright, now we start learning the Grunt and npm specifics. Any project that runs on Node.js requires a package.json. If you’ve never seen or used a JSON (JavaScript Object Notation) file before, don’t be too concerned. It’s basically JavaScript.

There’s a lot to know about package.json, but you can figure that out as you go along.

Go to your local directory. Mine is Users/tania/sites/startgrunt. Here’s the Terminal command I’ll use to move to that directory.

cd sites/startgrunt

Create a file and save it as package.json.

{
  "name": "startgrunt",
  "version": "0.0.1",
  "description": "Learning Grunt and Sass",
  "license": "MIT",
  "devDependencies": {
    "grunt": "~0.4.5",
    "grunt-contrib-sass": "latest",
    "grunt-postcss": "latest",
    "autoprefixer": "latest",
    "grunt-contrib-cssmin": "latest",
    "grunt-contrib-uglify": "latest",
    "grunt-contrib-watch": "latest"
  }
}

The first several entries are self-explanatory. name is the name of your project; you can give it a version number, description and a license to cover all your legal bases.

devDependencies are the plugins your project depends on to function. Package.json is very cool, because you can define them all here, and when you install npm, it will install all of your plugins along with it. I’m putting the current version of Grunt, which is 0.4.5. I’m just going to tell it to install the latest version of each individual plugin. I’m sure there might be a specific case where you’d want to install a specific, older version, but I can’t see why we would do that in this situation.

The packages we’re installing:

So, what if I created my package.json and installed npm but forgot to include postcss? Or decide later I want to add a new plugin to my project? No problem. This will be the code.

npm install --save-dev grunt-postcss

8. Create a local npm install in a local directory

Now you’ll create your local npm install, which will create a folder called node_modules in your directory. That folder will contain Grunt and all the Grunt plugins.

npm install
npm WARN package.json [email protected] No repository field. npm WARN package.json [email protected] No README data

This might come up, which doesn’t matter. Grunt wants you to have a README.md and a repository set. You can choose to create one and add it to your package.json, but it’s not required. Here’s the code for that.

  "repository": {
    "type": "git",
    "url": "https://github.com/taniarascia/startgrunt.git"
  }

Terminal will go through and install all the plugins, and then you’ll have your node_modules folder all set and ready to go.

Creating your Gruntfile

Everything that Grunt will do is to be defined by you in Gruntfile.js. This will be the final step in your Grunt process.

There are four parts: loading grunt, defining your tasks, loading the plugins, and registering the tasks.

Load Grunt

First, load Grunt. You don’t even have to think about this one; just paste it at the top.

module.exports = function (grunt) {
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
	});

Tasks

Tasks come next. We’ll get to the tasks after I explain the rest of the basic setup.

Load Grunt Plugins

With the loadNpmTasks functions, we’ll be loading in all the plugins we installed earlier with package.json. I won’t load the PostCSS add-ons here, as they go in the individual task.

// Load Grunt plugins
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');

Register Grunt tasks

Finally, register your task. The 'default' task is what will run when you type grunt into the command line in your local project. The only task I’m going to run is 'watch', because all the rest of my plugins will compile into that task. Close all your brackets at the end of the file.

	// Register Grunt tasks
	grunt.registerTask('default', ['watch']);
};

Here is your compiled Gruntfile.js skeleton.

// Load Grunt
module.exports = function (grunt) {
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
  // Tasks
	});
	// Load Grunt plugins
	grunt.loadNpmTasks('grunt-contrib-sass');
	grunt.loadNpmTasks('grunt-postcss');
	grunt.loadNpmTasks('grunt-contrib-cssmin');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');

	// Register Grunt tasks
	grunt.registerTask('default', ['watch']);
};

Project Setup

The project setup is going to be very simple. We just want to make sure it works. The specifics can come later. I’m just going to make an index.html that links to css/style.min.css and js/script.min.js. I’m going to make two Sass files to compile into one minified CSS file, and minify my script.js as well.

Here’s the directory setup.

Any Sass file (which has a .scss extension) is valid CSS, you’re good to go here even if you don’t know any Sass yet. The Sass Guide is a great introduction.

In my sass directory, I’m going to make these two files.

sass/_base.scss – setting variables.

// Variables

$header-size: 2em;
$font-color: #343434;
$content-width: 1200px;

sass/style.scss – importing variables and setting styles.

@import 'base';

main {
	max-width: $content-width;
  margin: 0 auto;
	color: $font-color;
  display: flex;
	h1 {
		font-size: $header-size;
	}
}

src/script.js – alert on button click.

(function ($) {
	$(function () {
		$('button').click(function () {
			alert("jQuery alert!");
		});
	});
})(jQuery);

index.html – putting it all together.

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<title>Start Grunt</title>
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link href="css/style.min.css" rel="stylesheet">
</head>

<body>
	<main>
		<h1>Start Grunt</h1>
		<p>Hello, world!</p>
		
		<button>Press me</button>
	</main>

	<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
	<script src="js/script.min.js"></script>
</body>

</html>

Grunt Tasks

We’re going to set up all the tasks. Anytime you see an asterisk, it means “apply to any file” in that directory.

Sass

I’m going to tell the Sass plugin to take any file in the sass directory, and compile it into the same named file in the css directory. I’m turning off the “sourcemap” option, because I don’t need it.

src will always be your source file location for development.
dist or dest, for distribution or destination, is where the final production file ends up.

sass: {
  dist: {
    options: {
      sourcemap: 'none'
    },
    files: [{
      expand: true,
      cwd: 'sass',
      src: ['**/*.scss'],
      dest: 'css',
      ext: '.css'
  }]
  }
},

PostCSS

I’m going to pull in autoprefixer to add vendor prefixes and minify my CSS. I’m also turning off the maps here.

postcss: { // Begin Post CSS Plugin
  options: {
    map: false,
    processors: [
  require('autoprefixer')({
        browsers: ['last 2 versions']
      })
]
  },
  dist: {
    src: 'css/style.css'
  }
},

CSSMin

CSSMin to minify CSS files.

cssmin: { // Begin CSS Minify Plugin
  target: {
    files: [{
      expand: true,
      cwd: 'css',
      src: ['*.css', '!*.min.css'],
      dest: 'css',
      ext: '.min.css'
}]
  }
},

UglifyJS

UglifyJS to minify JavaScript files.

uglify: { // Begin JS Uglify Plugin
  build: {
    src: ['src/*.js'],
    dest: 'js/script.min.js'
  }
},

Watch

Here’s the most important task of all (in my opinion) – the watch task. Everything that I’ve previously told Grunt to do, I will now stick into a single command. When I run this command, Grunt will watch for any file changes and apply all the plugins.

watch: { // Compile everything into one task with Watch Plugin
      css: {
        files: '**/*.scss',
        tasks: ['sass', 'postcss', 'cssmin']
      },
      js: {
        files: '**/*.js',
        tasks: ['uglify']
      }
    }

Here’s your completed Gruntfile.js.

// Load Grunt
module.exports = function (grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // Tasks
    sass: { // Begin Sass Plugin
      dist: {
        options: {
          sourcemap: 'none'
        },
        files: [{
          expand: true,
          cwd: 'sass',
          src: ['**/*.scss'],
          dest: 'css',
          ext: '.css'
      }]
      }
    },
    postcss: { // Begin Post CSS Plugin
      options: {
        map: false,
        processors: [
      require('autoprefixer')({
            browsers: ['last 2 versions']
          })
    ]
      },
      dist: {
        src: 'css/style.css'
      }
    },
    cssmin: { // Begin CSS Minify Plugin
      target: {
        files: [{
          expand: true,
          cwd: 'css',
          src: ['*.css', '!*.min.css'],
          dest: 'css',
          ext: '.min.css'
    }]
      }
    },
    uglify: { // Begin JS Uglify Plugin
      build: {
        src: ['src/*.js'],
        dest: 'js/script.min.js'
      }
    },
    watch: { // Compile everything into one task with Watch Plugin
      css: {
        files: '**/*.scss',
        tasks: ['sass', 'postcss', 'cssmin']
      },
      js: {
        files: '**/*.js',
        tasks: ['uglify']
      }
    }
  });
  // Load Grunt plugins
  grunt.loadNpmTasks('grunt-contrib-sass');
  grunt.loadNpmTasks('grunt-postcss');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-watch');

  // Register Grunt tasks
  grunt.registerTask('default', ['watch']);
};

Run grunt command

In the root of your directory, run this command in Terminal.

grunt

You’ll get this response.

Running "watch" task Waiting...

Now save a file in your sass directory.

>> File "sass/_base.scss" changed. Running "sass:dist" (sass) task Running "postcss:dist" (postcss) task >> 1 processed stylesheet created. Running "cssmin:target" (cssmin) task >> 1 file created. 100 B ? 72 B Done, without errors.

And save the JavaScript file in your src directory.

>> File "src/script.js" changed. Running "uglify:build" (uglify) task >> 1 file created.

That was a lot of steps. No wonder you didn’t want to do this before.

I’ve set up a GitHub repository of the project for you to play with.

View source on GitHub

Thank you for reading! I'm Tania Rascia, and I write no-nonsense guides for designers and developers. If my content has been valuable to you, please help me keep the site ad-free and continuously updated with quality tutorials and material by making a donation. Any amount helps and is greatly appreciated! Otherwise, let me know any ideas you have on a course you'd be eager to see.

Write a response

Your email address will not be published.

Discussion

  • Anonymous says:

    Hello Tania,

    I have one problem with the code.
    1. I make a change on scss
    2. The page is refreshed without the change
    3. The change gets compiled to the css file after a couple of seconds
    4. Then i have to manually refresh the page in order to see the page.

    Is there a way to refresh the browser on both scss and css change?

    Thx in advance,
    Nektarios

  • Anonymous says:

    very thorough tutorial, is it writen by a woman? yeeeah it is 🙂

  • Andrew Bruce says:

    Best tutorial all in one read. Thank you so much!

  • Reuben Mansell says:

    This is the best explanation of node/npm/grunt/sass setup. I really struggled to get my head around global and local installs, thanks for your explanation it’s so…human!

  • Racso says:

    “Any Sass file (which has a .scss extension) is valid CSS, you’re good to go here even if you don’t know any Sass yet”

    I think that sentence is wrong. You can’t take any Sass file and put it to work as a css file. It simply won’t work if it has a variable on it, for example.

    I believe you wanted to convey the opposite: any CSS file is valid SASS, so you can take any valid CSS file and start working in SASS with it.

  • Nowai says:

    Thank you so much for this! I was able to get grunt and sass up for my project. Great post!

  • Diogo MuSou says:

    Thank you! Nice tutorial! I got a little confused because there was no “src” directory in the image after “Here’s the directory setup.”, but I got it in the end! This is powerful stuff =)

  • Shubham says:

    Thank You for taking time and writing this article. It’s a very nice introduction to some of the fundamentals.

  • Patrick Moran says:

    Thanks Tania

    Currently I get an empty base.css and empty bas.min.css. is there a way to only have the compiled style.css files added to the CSS director

  • korksi says:

    Thanks tania for putting such effort in your articles, I really appreciate it !

  • Emily says:

    Hi Tania,
    Thanks for taking the time to create this tutorial.

    After running through the steps, when I try to save my .scss file at the end, I never end up getting the “>> File “sass/_base.scss” changed.” Which step would I have goofed on for this to happen? The src/script.js file worked and showed me the added message.

  • Chris says:

    Stating that npm is just a tool that installs stuff is plain wrong. Npm runs script too. Who needs the gulp/grunt abstraction layers anyway when you can run the underlying scripts directly with npm run?

    • korksi says:

      Could you be more specific? Or link to an article or post that addresses your concerns and opinion?
      As I see it this article deals with very basic stuff, so usually people who read this aren’t aware of alternatives or different point of views, so it would be nice to get more information if you so bluntly criticise someone that obviously puts a lot of effort in articles like these.
      I think it is good if you are able to point out obscurities or maybe even mistakes, but please substantiate your criticism with a little more data.
      Or to say it in your words: Who needs whiny comments anyway when you could just read something productive instead?

  • Mehdika says:

    Very easy to understand. good enough to get started immediately.
    Thank you for this article.

  • Anonymous says:

    Very simple to understand. Thank you for your tutorial.

  • jayakrishna says:

    very nice , clear .. i have come across many tutorials but none is like this.

  • Aphex says:

    Thanks Tania – very nice introduction to some of the fundamentals!

Tania's List

My tutorials, guides, and articles for web designers, developers, and autodidacts, sent out once a month or so. No bullshit, ads, or tricks.