Skip to content

Instantly share code, notes, and snippets.

@retlehs
Created February 26, 2015 15:04
Show Gist options
  • Save retlehs/b3b3850216b42b3e0761 to your computer and use it in GitHub Desktop.
Save retlehs/b3b3850216b42b3e0761 to your computer and use it in GitHub Desktop.
Roots documentation from pre-8.0.0

After reading through this guide, you will:

  • Understand the Roots Wrapper and recognize how it extends and complements the WordPress template hierarchy.
  • Know what is meant by the DRY Principle, why being DRY bests being WET, and see how most WordPress themes are WET.
  • Be able to filter the Roots Wrapper and create a new base template for a Custom Post Type.

Template Hierarchy

WordPress is pretty smart. Every time you load up a request it will search for the most relevant template available in your theme and load it. This is the Template Hierarchy in action and it enables us to easily customize the appearance of our sites.

Want to customize a specific page named "About"? Just copy page.php, to page-about.php and edit away to your heart's content. You only need to look at the success of WordPress to realize that this system works because of its simplicity and accessibility. But it doesn't work perfectly.

To prevent each new template from having a duplicate header, footer and sidebar, WordPress encourages you to separate that code into other templates and include them with the get_header(), get_footer() and get_sidebar() functions (all of which are based on get_template_part). Whilst this makes your code more manageable, with only one edit needed to implement a change across all templates, it still duplicates code that simply doesn't need to be duplicated; the code which calls each of those templates.

Wrapperless Templates

In your typical wrapperless theme, every page template will look something like the following;

<?php get_header(); ?>
  <div class="wrap">
    <div class="content">
      <?php // Our page specific markup and loop goes here ?>
    </div>
    <?php get_sidebar(); ?>
  </div>
<?php get_footer(); ?>

Even though we know that every template will take this base format and render the header, footer, sidebar calls each time, we still need to continuously repeat the code to keep WordPress happy; it's laborious and unnecessary.

Enter DRY

DRY simply means Don't Repeat Yourself and conforming to the DRY Principle means:

Every piece of knowledge must have a single, unambiguous, authoritative representation within a system. [1]

So whilst we have a base format for our pages, this "knowledge" is written countless times, spread across numerous files and has no authoritative representation. This is the opposite of DRY code and it's usually described as being WET, meaning that you Write Everything Twice.

As you can see from the wrapperless template, WordPress goes beyond writing everything just twice, which is why I prefer the term CRY or Continously Repeating Yourself [2]. Whatever you want to call it, it wastes your time when creating the code, when reading the code and when editing the code; it's a lose-lose-lose situation (plus repetition is only fun in rhetoric) but it's easy enough to avoid.

##Wrap Up and Stay DRY## The goal of a theme wrapper [3] is to remove any repeated markup from individual templates and put it into a single file. This file, base.php becomes the single, unambiguous, authoritative representation of knowledge (i.e. the base format code). By doing this we can put the focus entirely on the page specific markup and loop, simplifying our templates to look like this:

<?php // Our page specific markup and loop goes here ?>

It's neat. It's tidy. You never need to make calls to get_header(), get_footer() or get_sidebar() again. You can also refactor the base format of your site by editing base.php. But best of all, it takes less than 50 lines of code to do so.

Step 1: WordPress figures out which template to use

This is done using the standard WordPress Template Hierarchy, which, as I mentioned before, selects the most relevant template as our starting point. Once this template has been chosen, but before it's loaded, WordPress runs the template_include($template) filter.

We use this filter to run our wrap function that saves the $main_template path and $base as static variables in our Roots_Wrapping class;

<?php
  class Roots_Wrapping {
    // Stores the full path to the main template file
    static $main_template;

    // Stores the base name of the template file; e.g. 'page' for 'page.php' etc.
    static $base;

    static function wrap($main) {
      self::$main_template = $main;
      self::$base = basename(self::$main_template, '.php');

      if (self::$base === 'index') {
        self::$base = false;
      }

      return new Roots_Wrapping();
    }
  }
  add_filter('template_include', array('Roots_Wrapping', 'wrap'), 99);
?>

The wrap function also checks to see if the $base is index (in which case we know to use the default base.php) and finally returns a new instance of Roots_Wrapping.

Step 2: The theme wrapper selects which base to use

During the construction of our new Roots_Wrapping instance, we set a variable for the $slug (which defaults to base) and create a new $templates array with the fallback template base.php as the first item.

We then check to see if the $base exists (i.e. confirming we're not starting on index.php) and shift a more specific template to the front of the $templates array,

<?php
  public function __construct($template = 'base.php') {
    $this->slug = basename($template, '.php');
    $this->templates = array($template);

    if (self::$base) {
      $str = substr($template, 0, -4);
      array_unshift($this->templates, sprintf($str . '-%s.php', self::$base));
    }
  }
?>

We then use the magic __toString() function to apply a filter to our final $templates array, before returning the full path to the most specific existing base template via the inbuilt WordPress function locate_template.

<?php
  public function __toString() {
    $this->templates = apply_filters('roots_wrap_' . $this->slug, $this->templates);
    return locate_template($this->templates);
  }
?>

All this is done before any content is served; so at this point WordPress has given us a starting point, which we've changed to a base file, thus extending the template hierarchy by one step. We also fire off a filter, so that the Roots_Wrapper can be easily overridden or added to.

Step 3: The base-*.php file serves the content

Once the theme wrapper has used locate_template to select the appropriate base file, it will then start serving the base markup (grabbing the header, footer and sidebar code as it goes along). The content from the WordPress starting point i.e. $main_template will be loaded by the line:

<?php include roots_template_path(); ?>

This is a helper function that returns and loads the $main_template we saved earlier. You could also manually include the template from the base-*.php or just have all the code in that one file alone. See the footnotes for caveats [4].

Wrapping Things Up

Effectively we've started and ended with the standard WordPress Template Hierarchy, but grabbed the base format from the appropriate base file in between. The markup of our content is wrapped between our base markup, the cycle completes and the theme wrapper's job is now done. In fact, because the theme wrapper starts and ends with the output from the standard Template Hierarchy, the vast majority of issues can be resolved just by looking through and understanding the Template Hierarchy Codex Page.

Filtering the Wrapper: Custom Post Types

Let's say you wanted your base markup to change depending on what CPT WordPress was using. With the Roots Wrapper you just need to add the following to your lib/custom.php file (or anywhere else more appropriate):

<?php
  add_filter('roots_wrap_base', 'roots_wrap_base_cpts'); // Add our function to the roots_wrap_base filter

  function roots_wrap_base_cpts($templates) {
    $cpt = get_post_type(); // Get the current post type
    if ($cpt) {
       array_unshift($templates, 'base-' . $cpt . '.php'); // Shift the template to the front of the array
    }
    return $templates; // Return our modified array with base-$cpt.php at the front of the queue
  }
?>

Now when the base markup is loaded via return locate_template($this->templates); it will load our CPT template if it exists. When a CPT doesn't have its own template WordPress will simply revert to using our standard base.php file.

Comments, as always, are very welcome. Also don't forget to subscribe for more Roots related goodies!


  1. [1]: Coined in [The Pragmatic Programmer](http://pragprog.com/the-pragmatic-programmer) by Andy Hunt and Dave Thomas.
  2. [2]: Cry => Tears => Wet. Coined by Nick Fox, in this article, it won't catch on, but sometimes it's nice to be a footnote.
  3. [3]: The Roots Wrapper is based on [this post](http://scribu.net/wordpress/theme-wrappers.html) by Scribu. Scribu's code is in the public domain but credit where it's due. The Roots_Wrapping class has been modified and is MIT licensed.
  4. [4]: Please do not put all your code in a base-*.php unless you're certain you're being clever; if you only think you're being clever then it's not yet clever enough.

Theme Installation

Clone the git repo - git clone git://github.com/roots/roots.git - or download it and then rename the directory to the name of your theme or website.

If you don't use Bedrock, you'll need to add the following to your wp-config.php on your development installation:

define('WP_ENV', 'development');

Note: style.css is used only to tell WordPress that we’re a theme. You should change the values within this file to match your site and author details. Don’t place any CSS in this file since it’s not enqueued.

Theme Activation

Once you activate Roots from the WordPress admin you’ll be taken to the activation options which is handled by lib/activation.php:

  • Create static front page Create a page called Home and set it to be the static front page. This is normally handled from the Reading settings.
  • Change permalink structure Change permalink structure to /%postname%/. This is normally handled from the Permalinks settings.
  • Create navigation menu Create the Primary Navigation menu and set the location. This is normally handled from the Menus section under Appearance.
  • Add pages to menu Add all published pages to the Primary Navigation. On a fresh install, this will add the new Home page and existing Sample Page to the navigation.

Compiling CSS and JavaScript

Roots uses Grunt for compiling LESS to CSS, checking for JS errors, live reloading, concatenating and minifying files, versioning assets, and generating lean Modernizr builds.

To use it, install the required dependencies as directed and then run some Grunt commands.

Install Grunt

From the command line:

  1. Install grunt-cli globally with npm install -g grunt-cli.
  2. Navigate to the theme directory, then run npm install. npm will look at package.json and automatically install the necessary dependencies. It will also automatically run bower install, which references dependencies in bower.json.

When completed, you'll be able to run the various Grunt commands provided from the command line.

Unfamiliar with npm? Don't have node installed? Download and install node.js before proceeding.

Available Grunt commands

  • grunt dev — Compile LESS to CSS, concatenate and validate JS
  • grunt watch — Compile assets when file changes are made
  • grunt build — Create minified assets that are used on non-development environments

Read about how the Gruntfile in Roots works

Theme Configuration

lib/config.php is used to enable/disable theme features and set configuration values. The theme features that can be disabled include:

  • Clean up from Soil
  • Root relative URLs from Soil
  • Clean search URLs (/search/query instead of ?s=query) from Soil
  • Bootstrap markup for the WordPress gallery shortcode
  • Latest jQuery loaded via the Google CDN with local fallback

Configuration values that can be set include:

  • Google Analytics ID
  • .main HTML classes
  • .sidebar HTML classes
  • Define which templates/pages exclude the sidebar

lib/init.php is used to register navigation menus, sidebars, and define add_theme_support() for WordPress core functionality such as post thumbnails. You should also define your custom image sizes with add_image_size() and add any support for post formats in this file.

Theme Functionality

functions.php is used to include files from the lib/ directory which contains all of the theme functionality. Don’t place any custom code in this file — use it only for includes. You can place custom code in lib/extras.php.

Since Roots is a starter theme, it’s okay for you to modify files within lib/ to meet the needs of the site you’re building.

Loading CSS and JavaScript

lib/scripts.php is used to tell WordPress which CSS and JS files to enqueue.

Only one CSS file is enqueued: assets/css/main.min.css.

Scripts are enqueued in the following order:

  1. The latest jQuery via Google CDN with local fallback (if enabled in lib/config.php) in the header
  2. A lean Modernizr build in the header
  3. assets/js/scripts.min.js in the footer

jQuery is in the header to avoid potential conflicts with plugins. Modernizr is also loaded in the header and after stylesheets as recommended by Modernizr.

Controlling Sidebar Display

lib/sidebar.php contains the Roots_Sidebar class which determines whether or not to display the sidebar based on an array of conditional tags or page templates.

To control the display of the sidebar, edit roots_display_sidebar() in lib/config.php. You can also hook into the roots_display_sidebar() filter.

If you’re not yet familiar with controlling the sidebar display in Roots, read the Sidebar Guide.

Comments

lib/comments.php contains the Roots_Walker_Comment class which extends Walker_Comment and is used by templates/comments.php to list post comments. This file also includes template/comment.php for the markup used on a single comment.

Cleaner Navigation Menu Walker

lib/nav.php contains functionality that cleans up the navigation menu markup. Walker_Nav_Menu (WordPress default) example output:

<li id="menu-item-8" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-8"><a href="/">Home</a></li>
<li id="menu-item-9" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-9"><a href="/sample-page/">Sample Page</a></li>

Roots_Nav_Walker example output:

<li class="menu-home"><a href="/">Home</a></li>
<li class="menu-sample-page"><a href="/sample-page/">Sample Page</a></li>

Instead of the many different active class varieties that WordPress usually uses, only active is returned on active items.

Theme Templates

The markup in Roots is based off HTML5 Boilerplate with ARIA roles for accessibility and the hNews microformat for posts.

The theme root contains files that you can further extend with the normal WordPress Template Hierarchy:

  • 404.php — Error 404 page
  • base.php — The theme wrapper which wraps the base markup around all template files
  • index.php — Archive page (used by blog page, category archives, author archives and more)
  • page.php — Single page
  • single.php — Single post page
  • template-custom.php — An example single page template

These files include templates from the templates/ directory which is where you’ll be making most of your customizations:

  • templates/comment.php — Markup for a single comment, included from templates/content-single.php
  • templates/comments.php — Markup for the list of comments using the Bootstrap media object, included from templates/content-single.php
  • templates/content-page.php — Markup included from page.php
  • templates/content-single.php — Markup included from single.php
  • templates/content.php — Markup included from index.php
  • templates/entry-meta.php — Post entry meta information included from content-single.php
  • templates/footer.php — Footer markup included from base.php
  • templates/header.php — Header markup included from base.php
  • templates/page-header.php — Page title markup, included from most of the files in the theme root
  • templates/searchform.php — Search form markup
  • templates/sidebar.php — Sidebar markup, included from base.php

Extending Templates

Even with the theme wrapper, the normal WordPress Template Hierarchy is still in tact. Here’s some examples:

  • Copy index.php to author.php for customizing author archives
  • Copy index.php to home.php for customizing the Home page if you’re showing the latest posts (under Reading Settings) instead of a static front page
  • Copy index.php to archive-gallery.php for customizing the archive page for a custom post type registered as gallery
  • Copy page.php to front-page.php for customizing the static front page
  • Copy page.php to page-about.php for customizing a page called About

The Theme Wrapper Guide goes into depth about creating new base.php files, but if you’d like to have a customized base.php based off a certain template, just copy it to base-<template name>.php. You can add conditional statements to base.php whenever possible and should generally aim to not have multiple theme wrappers in your theme.

If you’re not yet familiar with theme wrappers, read the Introduction to the Roots Theme Wrapper.

Theme Assets (Stylesheets, Images, JavaScript, Bower Packages)

The assets directory contains all of your project’s LESS, CSS, images, and JavaScript. LESS and JavaScript are concatenated and minified with the Grunt build script.

Stylesheets

├── components
│   ├── _buttons.less
│   ├── _forms.less
│   ├── _media.less
│   └── _wp-classes.less
├── layouts
│   │── pages
│   │   └── _home.less
│   │── _footer.less
│   │── _general.less
│   │── _header.less
│   │── _pages.less
│   │── _posts.less
│   └── _sidebar.less
├── _bootstrap.less
├── _global.less
├── _variables.less
└── main.less

less/main.less imports Bootstrap along with all of the stylesheets. Use _variables.less to override Bootstrap's variables or to define your own.

As you add more components and layouts to your theme, create new files for them and then add new @import's in main.less.

Images

Place images used in your theme in the assets/img/ directory. When working with template files, you can reference the images with:

<img src="<?php echo get_template_directory_uri(); ?>/assets/img/example.jpg" alt="">

JavaScript

JavaScript is compiled in this order into scripts.min.js:

  1. Bootstrap plugins
  2. All files from assets/js/plugins/
  3. _main.js

To modify the JavaScript assets in your theme, open up Gruntfile.js and modify the jsFileList array.

Bower Packages

Roots uses Bower for managing Bootstrap, jQuery, Modernizr, and Respond.js. Installed packages live in the assets/vendor/ directory, per .bowerrc. To install a Bower package:

bower install --save <package-name>

This will install a package and add it to the bower.json file.

Next Steps

Do you have any questions or need help? Our new Roots forum is a great place to get answers from the Roots developers and other users in the Roots community.

If you haven’t already, make sure to look at the HTML5 Boilerplate Documentation for a guide to the HTML and a useful FAQ that includes information regarding the use of protocol relative URLs.

The Bootstrap Documentation provides information on all of the Bootstrap CSS, components, and JavaScript plugins that are included in Roots.

To get updates to the latest changes made to Roots, follow @retlehs and watch Roots on GitHub.

Additional Reading

After reading through this guide, you will:

  • Understand how to display or hide the sidebar in Roots.
  • Know how pass arguments to conditional tags with the Roots_Sidebar class.
  • Be able to use the roots/display_sidebar filter to force the display of the sidebar.

Displaying the Sidebar

Some pages absolutely need a sidebar to function properly, whereas others will have their aesthetic ruined by superfluous asides. Fortunately the Roots_Sidebar class in lib/sidebar.php gives you full control over this using a couple of arrays.

The First Array: Conditional Tags

When you first start developing with WordPress you'll quickly become familiar with the common conditional tags such as is_page, is_single and is_home and you'll soon realize there is a conditional tag for almost every possible scenario. Wisely, all these conditionals have been codified and can be found via the WordPress Codex page for Conditional Tags.

What all the conditional tags share in common is their boolean nature; they check criteria and return either true or false. Likewise the Roots_Sidebar class needs a boolean response to figure out whether or not to display the sidebar. The first array simply checks all the conditional tags and returns false, thus hiding the sidebar, when any of the conditional tags return true. This means that you only need to add conditional tags for the circumstances you do not want the sidebar shown.

The Default Config

If you open up /lib/config.php you can find the default first array:

array(
  'is_404',
  'is_front_page'
),

As you can see the two conditions we set to hide the sidebar are is_404 and is_front_page. If we wanted to hide the sidebar from pages, we can just add is_page to the bottom of the array:

array(
  'is_404',
  'is_front_page',
  'is_page'
),

Passing Arguments

If you only wanted to exclude one page from displaying the sidebar, you'll need to pass an additional argument through the array, namely the page id or the slug. To do this you need to pass the conditional and argument together as an array. The following would exclude a page with id of 42;

array(
  'is_404',
  'is_front_page',
   array('is_page', 42)
),

You could also pass the page slug with 'page-slug' or 'Page Title' (note the single brackets) should you need to. Or combine everything together by passing the arguments as an array:

array(
  'is_404',
  'is_front_page',
   array('is_page', array(42, 'page-slug', 'Page Title'))
),

Further Customization

If you need to use a combination of conditionals to determine whether or not to display the sidebar then you can even write your own function and call that in the first array; just make sure it returns true to hide the sidebar or false to display. You can also filter the class directly (see Filtering the Sidebar Class)

The Second Array: Page Templates

Displaying the sidebar will often depend on the page template that you are using. Rather than passing each template check as an array (as we did above in passing arguments) the second array is a dedicated is_page_template check.

By default the only template check is for the Roots full width custom template but you can add as many as you like. As with the conditionals check, a match (returning true) will hide the sidebar:

array(
  'template-custom.php',
  'page-contact.php',
  'folder/custom.php',
  'page-42.php'
)

Just remember that this only checks against the main page template, i.e. the page template selected by the WordPress template hierarchy, via the template redirect action or the template_include filter; it will not check every file included via include or get_template_part.

Filtering the Sidebar Class

To hide the sidebar you need just one true returned in all of your conditional and page template checks. This makes it very easy to hide the sidebar for a large number of pages, or for just one.

Unfortunately it's not so easy to hide the sidebar for a large number of pages, but display it on just one. This is where the filter comes in. Overriding the output of the Sidebar_Class means you can force the sidebar to be shown, even when your conditional and template checks would otherwise hide it.

Let's say you wanted the sidebar hidden on every page, so you add is_page to the first array. Later in development you suddenly realize that on you need a sidebar on just one of the pages;

add_filter('roots/display_sidebar', 'roots_sidebar_on_special_page');

function roots_sidebar_on_special_page($sidebar) {
  if (is_page('special-page')) {
    return true;
  }
  return $sidebar;
}

The first line adds our function to the filter, and the function simply uses the is_page conditional tag to determine if we're on the special page. Is so we force a true response, overriding the setting determined by the first two arrays.

Note that at this point the logic has switched, with a return value of true displaying the sidebar and false being used to hide it. This is because we are not performing an individual check, but instead returning a definitive yes or no to the sidebar.

Like what you've read? Then don't miss my post on the Roots Theme Wrapper. We'll be using the wrapper to load a custom sidebar template in a post coming up soon.

Using Grunt with Roots will help you automate your workflow and build WordPress themes more efficiently.

The Roots Gruntfile will:

  1. Validate your JavaScript with JSHint
  2. Compile and minify Bootstrap’s bootstrap.less along with your site-specific main.less to main.min.css
  3. Concatenate and minify all your JavaScript plugins and site-specific JS to scripts.min.js
  4. Update lib/scripts.php’s wp_enqueue_style and wp_enqueue_script calls to update the version based off the md5 of the files (to bust cache)

Benefits

  • Easily compile LESS files
  • Minification and concatenation without plugins
  • Fewer requests made to the server (one CSS file, one main JS file besides Modernizr and jQuery)
  • Ensures valid JavaScript
  • Others working on your project are able to use the same build script and have a unified development process
  • Code is optimized for production use

The Gruntfile.js file

'use strict';
module.exports = function(grunt) {
  // Load all tasks
  require('load-grunt-tasks')(grunt);
  // Show elapsed time
  require('time-grunt')(grunt);

  var jsFileList = [
    'assets/vendor/bootstrap/js/transition.js',
    'assets/vendor/bootstrap/js/alert.js',
    'assets/vendor/bootstrap/js/button.js',
    'assets/vendor/bootstrap/js/carousel.js',
    'assets/vendor/bootstrap/js/collapse.js',
    'assets/vendor/bootstrap/js/dropdown.js',
    'assets/vendor/bootstrap/js/modal.js',
    'assets/vendor/bootstrap/js/tooltip.js',
    'assets/vendor/bootstrap/js/popover.js',
    'assets/vendor/bootstrap/js/scrollspy.js',
    'assets/vendor/bootstrap/js/tab.js',
    'assets/vendor/bootstrap/js/affix.js',
    'assets/js/plugins/*.js',
    'assets/js/_*.js'
  ];

  grunt.initConfig({
    jshint: {
      options: {
        jshintrc: '.jshintrc'
      },
      all: [
        'Gruntfile.js',
        'assets/js/*.js',
        '!assets/js/scripts.js',
        '!assets/**/*.min.*'
      ]
    },
    less: {
      dev: {
        files: {
          'assets/css/main.css': [
            'assets/less/main.less'
          ]
        },
        options: {
          compress: false,
          // LESS source map
          // To enable, set sourceMap to true and update sourceMapRootpath based on your install
          sourceMap: true,
          sourceMapFilename: 'assets/css/main.css.map',
          sourceMapRootpath: '/app/themes/roots/'
        }
      },
      build: {
        files: {
          'assets/css/main.min.css': [
            'assets/less/main.less'
          ]
        },
        options: {
          compress: true
        }
      }
    },
    concat: {
      options: {
        separator: ';',
      },
      dist: {
        src: [jsFileList],
        dest: 'assets/js/scripts.js',
      },
    },
    uglify: {
      dist: {
        files: {
          'assets/js/scripts.min.js': [jsFileList]
        }
      }
    },
    autoprefixer: {
      options: {
        browsers: ['last 2 versions', 'ie 8', 'ie 9', 'android 2.3', 'android 4', 'opera 12']
      },
      dev: {
        options: {
          map: 'assets/css/'
        },
        src: 'assets/css/main.css'
      },
      build: {
        src: 'assets/css/main.min.css'
      }
    },
    modernizr: {
      build: {
        devFile: 'assets/vendor/modernizr/modernizr.js',
        outputFile: 'assets/js/vendor/modernizr.min.js',
        files: {
          'src': [
            ['assets/js/scripts.min.js'],
            ['assets/css/main.min.css']
          ]
        },
        uglify: true,
        parseFiles: true
      }
    },
    version: {
      default: {
        options: {
          format: true,
          length: 32,
          manifest: 'assets/manifest.json',
          querystring: {
            style: 'roots_css',
            script: 'roots_js'
          }
        },
        files: {
          'lib/scripts.php': 'assets/{css,js}/{main,scripts}.min.{css,js}'
        }
      }
    },
    watch: {
      less: {
        files: [
          'assets/less/*.less',
          'assets/less/**/*.less'
        ],
        tasks: ['less:dev', 'autoprefixer:dev']
      },
      js: {
        files: [
          '<%= jshint.all %>'
        ],
        tasks: ['jshint', 'concat']
      },
      livereload: {
        // Browser live reloading
        // https://github.com/gruntjs/grunt-contrib-watch#live-reloading
        options: {
          livereload: false
        },
        files: [
          'assets/css/main.css',
          'assets/js/scripts.js',
          'templates/*.php',
          '*.php'
        ]
      }
    }
  });

  // Register tasks
  grunt.registerTask('default', [
    'dev'
  ]);
  grunt.registerTask('dev', [
    'jshint',
    'less:dev',
    'autoprefixer:dev',
    'concat'
  ]);
  grunt.registerTask('build', [
    'jshint',
    'less:build',
    'autoprefixer:build',
    'uglify',
    'modernizr',
    'version'
  ]);
};

Installing Grunt to use with Roots

From the command line:

  1. Install grunt-cli globally with npm install -g grunt-cli.
  2. Navigate to the theme directory, then run npm install. npm will look at package.json and automatically install the necessary dependencies. It will also automatically run bower install, which references dependencies in bower.json.

When completed, you'll be able to run the various Grunt commands provided from the command line.

Unfamiliar with npm? Don't have node installed? Download and install node.js before proceeding.

Available Grunt commands

  • grunt dev — Compile LESS to CSS, concatenate and validate JS
  • grunt watch — Compile assets when file changes are made
  • grunt build — Create minified assets that are used on non-development environments

Get the Screencast $10

Topics Covered

You'll learn how to install and run Grunt and understand everything that's happening within the Roots Gruntfile.js. Learn how to use LiveReload and make changes to the Gruntfile. You'll also see how to use Bootstrap LESS mixins in your stylesheet and how to include JavaScript plugins in your theme.

  • Intro to Grunt / Why use a build tool?
  • How does Grunt work?
  • Installing Grunt & Roots dependencies
  • Looking into the Roots Gruntfile.js
  • Modifying LESS and JavaScript in your theme
Preview & More Info
  • Length: 24 min
  • No DRM - H264, AAC, 720p
  • 273 MB
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment