June 03, 2013

Jekyll 201: Beyond Hello World

There is plenty of material online showing you how to setup a Jekyll blog, write your first post and add a custom domain. But rarely do these tutorials go past the "Hello World" stage.

So it's time for Jekyll 201!

I've been using Jekyll for blogs, meetup groups, prototyping, and microsites for nearly 3 years so I wanted to share some more advanced techniques that aren't covered in the docs.

Partials

The basics of partials (includes in Jekyll parlance) are covered in the docs: http://jekyllrb.com/docs/templates/#includes

If you are coming from a traditional web development background, you might want to know how to pass in data to a partial. Maybe you have some book recommendations in your sidebar, like so:

<ul id="book-recommendations">
  <li class="recommendation">
    <img src="/img/pragmatic-programmer.png" />
    <a href="http://pragprog.com">Pragmatic Programmer</a>
    <span>My favorite book about programming pragmatically</span>
  </li>

  <li class="recommendation">
    <img src="/img/getting-real.png" />
    <a href="http://gettingreal.37signals.com">Getting Real</a>
    <span>A book about Getting Real and stuff...</span>
  </li>
</ul>

You can make a partial (e.g. _includes/recommendation.html) so you aren't copy-pasting HTML code:

<li class="recommendation">
  <img src="{{ img_url }}" />
  <a href="{{ url }}">{{ name }}</a>
  <span>{{ summary }}</span>
</li>

And then use it like so:

<ul id="book-recommendations">
  {% assign img_url = "/img/pragmatic-programmer.png" %}
  {% assign url = "http://pragprog.com" %}
  {% assign name = "Pragmatic Programmer" %}
  {% assign summary = "My favorite book about programming pragmatically" %}
  {% include recommendation.html %}

  {% assign img_url = "/img/getting-real.png" %}
  {% assign url = "http://gettingreal.37signals.com" %}
  {% assign name = "Getting Real" %}
  {% assign summary = "A book about Getting Real and stuff..." %}
  {% include recommendation.html %}
</ul>

We can re-use the HTML template but this is still not so great. Liquid doesn't have the ability to pass in a hash of local objects (like in Rails), instead we have to rely on essentially global variables.

Abusing _config.yml

A better way to handle the above case of book recommendations is to store the underlying data in _config.yml. This file is typically used to store configuration options for Jekyll, but any extra YAML data you put in _config.yml will be accessible in templates via the site variable.

So the _config.yml would look like this:

# General Jekyll Config
auto: true
pygments: true
exclude: [".gitignore", "readme.md", "Gemfile", "Gemfile.lock"]

# Book recommendations
recommendations:
  - img_url: /img/pragmatic-programmer.png
    url: http://pragprog.com
    name: Pragmatic Programmer
    summary: |
      My favorite book about programming pragmatically

  - img_url: /img/getting-real.png
    url: http://gettingreal.37signals.com
    name: Getting Real
    summary: |
      A book about Getting Real and stuff...

Caveats: changes to this file are not picked up when running Jekyll locally with the --auto flag, so you need to regenerate the site manually for changes to take effect (several hours of my life I will never get back).

Make sure all of your data is valid YAML (here is a good linter).

Then in your template, you can loop over all the book recommendations.

<ul id="book-recommendations">
  {% for recommendation in site.recommendations %}
    {% include recommendation.html %}
  {% endfor %}
</ul>

And update the partial to use properties on recommendation...

<li class="recommendation">
  <img src="{{ recommendation.img_url }}" />
  <a href="{{ recommendation.url }}">{{ recommendation.name }}</a>
  <span>{{ recommendation.summary }}</span>
</li>

Much better! And now, when you want to add a new book, just add it to the list in _config.yml and it will get rendered when you regenerate the site.

I've used this technique several times with success, here are a few examples: * Indy Startup Lab: listing the members and projects, repo here * Let's Work Happier: microsite with app directory, repo here

Adding extra fields to posts

Pages have some default attributes that you can set in the Front-matter; these include the basics like permalink, title, categories, and tags.

But you are not limited to just these fields. You can add any arbitrary data to the Front-matter (it is just a block of YAML after all) and they will be exposed on the page variable.

Let's say you write reviews on your blog sometimes and use Amazon Affiliates. Per the affiliate guidelines, you need a disclaimer on your post stating that you are participating in the program or the FCC will get mad.

You could copy and paste it to the bottom of each Markdown post — but a better way would be to set a custom attribute in the Front Matter.

---
layout: post
title: "My Favorite Gadgets Reviewed"
disclaimer: true
---

By adding disclaimer to the Front-matter, we can use page.disclaimer in the post layout to display our affiliate text at the bottom of the post when necessary.

  {% if page.disclaimer %}
    <div class="disclaimer">
      <p>
        Disclaimer: Matt Swanson is a participant in the Amazon Services LLC 
        Associates Program, an affiliate advertising program designed to provide 
        a means for sites to earn advertising fees by advertising and linking 
        to amazon.com
      </p>
    </div>
  {% endif %}

This technique is pretty powerful, here a few other ideas you can use it for: * Author attribution: useful for multiple author blogs/sites * Enable/disable Disqus/LiveFyre comments on a per-post basis * Add summary, subtitles, per-post header images * Display a banner for your new startup at the bottom of posts

Ghetto Asset Pipeline

A little trick I picked up from the DevelopmentSeed blog — you can use includes to concatenate your CSS files.

Move your CSS files into the _includes folder (I like to put them in a css sub-folder). Then make an all.css file with an empty Front Matter, here is the one from the blog you are currently reading.

---
---
{% include css/font-awesome.css %}
{% include css/base.css %}
{% include css/skeleton.css %}
{% include css/screen.css %}
{% include css/layout.css %}
{% include css/syntax.css %}

VoilĂ ! Six asset requests down to one!

Custom Plugins

The new Jekyll docs have a great section on writing plugins with several examples so I won't dive into it here.

Be warned — you cannot use custom plugins with GitHub pages. I am of the opinion that GitHub pages is a huge benefit to using Jekyll, so be careful about relying on plugins.

Often times you can reproduce the functionality with Javascript or a Modular Component (see below). Personally, whenever I reach for a custom plugin I step back and evaluate whether I really need the functionality at all — usually the answer is no.

Building Modular Components

Static sites, by design, are very limited. In a world where you can build an entire business on a WordPress blog, Jekyll seems very simplistic.

For the most part, I think this is good — I believe in putting the emphasis back on content. But there are times when you miss some of the conveniences of heavier content platforms.

You can add some of this functionality back by using Modular Components. The basic concept is to write smaller services that your blog can interact with. By offloading the backend work to other apps you can get around some of the limits of a static site.

The most discussed example is using a 3rd party comment system — so I will skip that. Two other use cases that I think are more interesting are an email form and a web-based post editor.

For the email form, I created a simple Ruby app that accepts cross-domain POSTs and sends emails to the officer list for a local meetup. The Jekyll site just serves up the form with the action pointing to the mailer app. Not perfect, but certainly better than setting up a whole CMS.

Prose is an app that allows you to edit and create posts on GitHub pages with a web interface by connecting to the GitHub API. I personally still stick with the command line and SublimeText for writing my posts, but those of you that miss having a WYSIWYG web editor for your content should check out Prose.


Are there more Jekyll topics you want to see covered? Hit me up on Twitter.