Unleash Mr.Hyde! Introduction of Jekyll-Assets.

Not so long ago I have released Jekyll-Assets plugin, that adds Rails-alike assets pipeline for Jekyll or Octopress powered blogs. As later Brandon Mathis mentioned, a reference to “rails-alike” gives almost zero understanding of what plugin actually does. In few words, with this plugin you can write your assets in languages like CoffeeScript, SASS, LESS, automagically minify and concatenate assets, and more ;)) So this is an introduction to some of core features and how to enable jekyll-assets for your blog…

As I said before jekyll-assets is a plug-in for Jekyll and Octopress. Setup process is the same for both of them, so examples will be shown on a Jekyll-based blog, but instructions are quiet similar for Octopress users with few differences in “default” paths you have to use.

For visual demonstration I’ll be using an example blog. It is under git control, so you can always see history of it’s evolution.

First of all let’s take a quick look at what we are going to do. Assume we have a very simple blog with some posts. And we’re gonna style it up with Twitter’s Bootstrap framework. And we are going to add some search by tags feature using typeahead jQuery plugin from Bootstraps' toolbox. Also we are going to write our own assets in SASS and CoffeeScript.

Preparations

As we are going to have our javascript assets to be written in CoffeeScript and, we will need ExecJS-supported runtime on system to invoke it.

That means that you’ll need to have Node and CoffeeScript Node module installed. Also, probably, you would like to have UglifyJS Node module installed as well to have minification (alternatively you can use YUI compressor for this purpose).

Initial state

Before we start, let’s overview initial structure of our blog:

.
├── _assets/
│   └── javascripts/
│       └── vendor/
├── _config.yml
├── Gemfile
├── Gemfile.lock
├── index.html
├── _layouts/
│   ├── default.html
│   └── post.html
├── _posts/
│   ├── 2012-01-18-it-s-the-final-countdown-the-only.md
│   └── ...
└── README.md

Installation

First of all we’ll need to add some gems into Gemfile:

#
# jekyll-assets plugin itself
#

gem "jekyll-assets"

#
# Additional gems for jekyll-assets
#

gem "coffee-script" # We want to write our javascripts in CoffeeScript
gem "uglifier"      # And we want our javascripts to be minified with UglifyJS
gem "sass"          # And we want to write our stylesheets using SCSS/SASS

Let’s slow down and take a closer look on the required gems above:

After you have put these lines into your Gemfile, run bundle to install dependencies. Now you are ready to enable plug-in. That’s really simple, just add following line into your _plugins/ext.rb file (create it if you don’t have such one yet):

require "jekyll-assets"

That’s all. Installation is complete. Now let’s add some assets.

Some basic assets

By default, jekyll-assets will search for assets under following self-describing paths (relative to your sources root):

So let’s create these directories and some base assets.

First of all we will put noise.png (nice background lovely generated with noise generator) under _assets/images directory.

Now let’s add some vendors:

OK. Now let’s put a smile on that face style our blog a little bit. To do so we’ll create a simple _assets/stylesheets/app.css.sass file first with something like this:

body
  background-color: #eee
  background-image: url(asset_path('noise.png'))

Notice that despite the fact that noise.png is kept under different directory we refer it using so-called “logical path” which is relative to the paths where jekyll-assets will look for assets. Let’s include bootstrap, the easiest way would be to add a line like this one to the op of our main stylesheet file:

//= require vendor/bootstrap/bootstrap

But first of all that looks ugly, secondly, we don’t need “full” bootstrap. So we will require vendor/bootstrap instead, will create this file as _assets/stylesheets/vendor/bootstrap.css.scss and will include only those parts we interested in:

@import "bootstrap/variables";
@import "bootstrap/mixins";

// CSS Reset
@import "bootstrap/reset";

// Grid system and page structure
@import "bootstrap/scaffolding";
@import "bootstrap/layouts";

// Base CSS
@import "bootstrap/type";
@import "bootstrap/forms";

// Components: common
@import "bootstrap/dropdowns";

// Components: Nav
@import "bootstrap/navs";
@import "bootstrap/navbar";

// Components: Misc
@import "bootstrap/labels-badges";

// Utility classes
@import "bootstrap/utilities";

So, let’s mixin helpers of Bootstrap into our main stylesheet and make it even more funky. Here’s what our app.css.sass will look like after all:

//= require vendor/bootstrap


@import "vendor/bootstrap/mixins"


body
  background-color: #eee
  background-image: url(asset_path('noise.png'))


#content
  background-color: #fff
  border: 1px solid #ccc

  @include border-radius(6px)
  @include box-shadow(0 1px 3px rgba(0,0,0,.1))

  margin: 25px auto
  padding: 25px
  width: 940px


#js-tag-search
  display: none


.js
  body
    @include opacity(0)

    &.is-ready
      @include opacity(100)
      @include transition(opacity 1s)

  #js-tag-search
    display: block

Now, when our styles are ready, let’s add some basic JavaScript file as well. Let’s create _assets/javascripts/app.js.coffee with following content:

#= require vendor/jquery

$ ->
  $("body").addClass "is-ready"

Hooray! We’re ready to use these assets in our layout. But before we’ll continue here’s an overview of our assets:

./_assets/
├── images/
│   └── noise.png
├── javascripts/
│   ├── app.js.coffee.erb
│   └── vendor/
│       ├── jquery.js
│       └── modernizr.js
└── stylesheets/
    ├── app.css.sass
    └── vendor/
        ├── bootstrap/
        │   ├── _accordion.scss
        │   └── ...
        └── bootstrap.css.scss
Using assets

Here’s how our layout will look like:

<!DOCTYPE html>
<html>
  <head>
    <title>My Blog</title>
    {% stylesheet app %}
    <script type="text/javascript">{% asset vendor/modernizr %}</script>
  </head>
  <body>
    <div id="content">
      <div class="container">
        {{ content }}
      </div>
    </div>

    {% javascript app %}
  </body>
</html>

I believe that this example is simple enough to understand, but here'a a quick overview of what is happening there:

Notice, that we may avoid file extension when we use stylesheet or javascript liquid tags, so the above are shortcuts for:

Also, notice, that despite that our asset files are in fact have .coffee extension, we call this file as app.js anyway. So we can say that we use “logical” extension. To better understand this concept, all these files will be treated as foobar.js:

foobar.js.coffee
foobar.js.coffee.erb
foobar.coffee
foobar.coffee.erb

Same thing about *.css. This is so called pipeline. So foobar.js.coffee.erb will be first rendered with ERB, then it will be compiled with CoffeeScript. And CoffeeScript is a JavaScript type, so we can avoid .js extension, but we leave it for readability.

Here’s list of Liquid tags you might want to use in your templates:

Helpers available inside assets

You can use following helpers/methods inside your ERB, SASS and SCSS files:

Let’s go crazy now!

As final step with asets let’s make our app.js more dynamic and add list of our posts with inks, titles and tags to allow search throught them with JavaScript. For such crazy thing you will need ERB, rename app.js.coffee to app.js.coffee.erb and now you can write something like this inside of it:

console.log <%= JSON.dump site.posts.map { |p| p.to_liquid["title"] } %>

Notice, that to use JSON you will ned to require it first. It’s a standard Ruby library s no need for extra step despite adding follwoing line into your _plugins/ext.rb file:

require "json"

Enough talking, let’s create something useful. We’ll add a navigation bar (from Bootstrap) with search field into our layout like so:

<div class="navbar navbar-static-top">
  <div class="navbar-inner">
    <div class="container">
      <a href="/" class="brand">My Blog</a>

      <form id="js-tag-search" class="form-search navbar-form pull-right">
        <input type="text" class="input-medium search-query"
               placeholder="Type tags to search" />
      </form>
    </div>
  </div>
</div>

Similar to stylesheets, we will save all jQuery plugins from Bootstrap into _assets/javascripts/vendor/bootstrap:

./_assets/
├── images/
│   └── noise.png
├── javascripts/
│   ├── app.js.coffee.erb
│   └── vendor/
│       ├── bootstrap/
│       │   ├── bootstrap-affix.js
│       │   └── ...
│       ├── jquery.js
│       └── modernizr.js
└── stylesheets/
    ├── app.css.sass
    └── vendor/
        ├── bootstrap/
        │   ├── _accordion.scss
        │   └── ...
        └── bootstrap.css.scss

Then we’ll add require bootstrap-typeahead directive into our main file with some initialization code, and finally our app.js.coffee.erb will look like:

#= require vendor/jquery
#= require vendor/bootstrap/bootstrap-typeahead

$ ->
  $("body").addClass "is-ready"

  $("#js-tag-search input").typeahead
    source: <%=
      JSON.dump site.posts.map { |p|
        [p.to_liquid["title"], p.url, p.tags.join(",")]
      }
    %>
    sorter: (items) ->
      $.map items, (o) ->
        JSON.stringify o
    matcher: (item) ->
      0 <= item[2].indexOf @query
    updater: (item) ->
      item = JSON.parse item
      window.location = item[1]
      null
    highlighter: (item) ->
      item = JSON.parse item
      "<a href=\"#{item[1]}\">#{item[0]}</a>"

That’s all! You can start jekyll server now to see it in action.

The world is not enough

It’s time to say jekyll-assets to minify our compiled assets. It’s as easy as adding few lines into your _config.yml file:

assets:
  compress:
    css:  sass
    js:   uglifier

For detailed information about possible configuration options refer jekyll-assets or contact me.

comments powered by Disqus