Modernizing Your WordPress Workflow

with Grunt & Bower

What We'll Cover

  • Getting used to the terminal
  • Managing project plugins & frameworks with Bower
  • Creating tasks in Grunt that will process our CSS, minify and concatenate our JavaScript, optimize images, and refresh our browser instantly

Common Problems
for Theme Developers

  • HTML / Template Management
  • CSS Management
  • JavaScript Management
  • CSS / JavaScript Concatenation / Minification
  • Image Optimization
  • Live Browser Updating
  • Local Server Environment

Tools to Help with These Problems

  • HAML, JADE, SLIM, Markdown
  • LESS, SASS, Stylus
  • CoffeeScript, LiveScript
  • WordPress Plugins, GUI Apps - CodeKit, Koala, Hammer
  • MAMP, LAMP, XAMPP, AMPPS, Vagrant

Problems Bower Solves

JavaScript Plugin / Framework Management & Updating

Problems Grunt Solves

All the Rest

Grunt Advantages Over GUIs

  • Portable with Project
  • Configurable for Multiple Environments (dev, dist)
  • Every Detail is Customizable
  • It's Free

Folder Structure


html             // your public folder
assets           // the files you will be editing
└─ less
└─ js
└─ img
bower_components // home to your Bower packages
node_modules     // home to your Node packages
tmp              // holds concatenated js to lint
bower.json       // the list of your Bower packages
package.json     // the list of your Node packages
Gruntfile.js     // where the Grunt magic happens
.jshintrc        // your JS Hint config file (optional)
					

Terminal Setup for Fun++

  1. Get iTerm2.
  2. Install OhMyZsh with
    
    curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh
    							
  3. Install Homebrew with
    
    ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)
    							
  4. Get Homebrew up to date and clean by running brew update and brew doctor, then add it to the path with
    
    export PATH="/usr/local/bin:$PATH" >> ~/.bash_profile
    							
  5. Install Node.js with brew install node
  6. Install the Grunt CLI with npm install -g grunt-cli
  7. Install Bower with npm install -g bower

Bower Packages

Create a file called bower.json.


{
  "name": "your-project-name",
  "version": "1.0.0",
  "dependencies": {
    
  }
}
						

Node (Grunt) Packages

Create a file called package.json.


{
  "name": "your-project",
  "version": "1.0.0",
  "dependencies": {

  }
}
						

Grunt Configuration

Create a file called Gruntfile.js.


'use strict';
module.exports = function (grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // package options will go here

  });

  // register tasks here
  
  grunt.registerTask('default', [
    
    // default actions go here
		
  ]);

};
						

Packages

Get the Bower packages you want from their registry.

CSS Preprocessing

You'll want the LESS, SASS, or some other package if your CSS preference is different.


npm install grunt-contrib-less --save-dev
						

or


npm install grunt-contrib-compass --save-dev
						

JavaScript Linting

Get JSLint.


npm install grunt-jslint --save-dev
						

JavaScript File Concatenation

You'll want this.


npm install grunt-contrib-concat --save-dev
						

JavaScript Minification

Get Uglify.


npm install grunt-contrib-uglify --save-dev
						

Error Notifications

Try Grunt Notify.


npm install grunt-notify --save-dev
						

Image Optimization

I like Imagemin.


npm install grunt-contrib-imagemin --save-dev
						

Live Updating

You want to use Watch. For updating CSS and JavaScript in the browser without refreshing the page, get the Chrome extension LiveReload.


npm install grunt-contrib-watch --save-dev
						

Server

Want to set up a node.js server for your project and ditch MAMP? Get Express.

For WordPress, you'll want the PHP version.

With the packages you want registered, the 'Load Tasks' section of your file should look something like this:


// Load tasks
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-notify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-contrib-compass');
					

Configuring Options


// package options
jshint: {
  options: {
    jshintrc: '.jshintrc' // jshint config file
  },
  all: [
    'Gruntfile.js',
    'assets/js/*.js'
  ]
},
concat: {
  basic: {
    src: [
      'bower_components/jquery/dist/jquery.js',
      'bower_components/foundation/js/foundation/foundation.js',
      'assets/js/main.js'
    ],
    dest: 'tmp/app.js'
  },
  extras: {
    src: [
      'bower_components/modernizr/modernizr.js'
    ],
    dest: 'tmp/modernizr.js'
  }
},
compass: {
  dist: {
    options: {
      config: 'config.rb'
    }
  }
},
imagemin: {
  dynamic: {
    files: [{
      expand: true,
      cwd: 'assets/img/',
      src: ['**/*.{png,jpg,gif}'],
      dest: 'public/build/img/'
    }]
  }
},
uglify: {
  build: {
    files: {
      'public/build/js/modernizr.min.js' : 'tmp/modernizr.js',
      'public/build/js/app.min.js' : 'tmp/app.js'
    }
  }
},
clean: {
  dist: [
    'tmp/**',
    'public/build/img/**'
  ]
},
					

Watch

A sample configuration:


watch: {
  compass: {
    files: ['assets/sass/**/*.{scss,sass}'],
    tasks: ['compass']
  },
  css: {
    files: ['public/build/css/*'],
    options: {
      livereload: true
    }
  },
  js: {
    files: [
      'assets/js/*.js'
    ],
    tasks: ['concat', 'uglify'],
    options: {
      livereload: true,
      atBegin: true
    }
  },
  imagemin: {
    files: [
      'assets/img/**'
    ],
    tasks: ['imagemin'],
    options: {
      livereload: true,
      atBegin: true
    }
  }
}
					

Register Default Tasks


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

Putting it All Together

With all of these modules registered and configured, your Gruntfile.js should look something like this:


module.exports = function (grunt) {
  'use strict';

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    // package options
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [
        'Gruntfile.js',
        'tmp/js/*.js'
      ]
    },
    concat: {
      basic: {
        src: [
          'bower_components/jquery/dist/jquery.js',
          'bower_components/foundation/js/foundation/foundation.js',
          'assets/js/main.js'
        ],
        dest: 'tmp/app.js'
      },
      extras: {
        src: [
          'bower_components/modernizr/modernizr.js'
        ],
        dest: 'tmp/modernizr.js'
      }
    },
    compass: {
      dist: {
        options: {
          config: 'config.rb'
        }
      }
    },
    imagemin: {
      dynamic: {
        files: [{
          expand: true,
          cwd: 'assets/img/',
          src: ['**/*.{png,jpg,gif}'],
          dest: 'public/build/img/'
        }]
      }
    },
    uglify: {
      build: {
        files: {
          'public/build/js/modernizr.min.js' : 'tmp/modernizr.js',
          'public/build/js/app.min.js' : 'tmp/app.js'
        }
      }
    },
    clean: {
      dist: [
        'tmp/**',
        'public/build/img/**'
      ]
    },
    watch: {
      compass: {
        files: ['assets/sass/**/*.{scss,sass}'],
        tasks: ['compass']
      },
      css: {
        files: ['public/build/css/*'],
        options: {
          livereload: true
        }
      },
      js: {
        files: [
          'assets/js/*.js'
        ],
        tasks: ['concat', 'uglify'],
        options: {
          livereload: true,
          atBegin: true
        }
      },
      imagemin: {
        files: [
          'assets/img/**'
        ],
        tasks: ['imagemin'],
        options: {
          livereload: true,
          atBegin: true
        }
      }
    }
  });

  // Load tasks
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-notify');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-imagemin');
  grunt.loadNpmTasks('grunt-contrib-compass');

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

};
					

Go Play

Follow Me

Twitter

@alancrissey

GitHub

TheRealAlan

CodePen

TheRealAlan

Website

alancrissey.com