Alex's Home Page

Blog Tech pt. 2

...on themes and layouts with Jekyll

Last time we covered blog tech (in Blog Tech pt. 1), we outlined how to use Jekyll to create a blog by 'baking' static files formatted according to certain conventions into a blog that doesn't need a database to retrieve data and posts. This jives nicely with my own needs as a small (but growing!) blog, totally customized at every step of along the way.

Also, WordPress and Joomla are the worst. So consider some of these posts as contributions towards the ultimate goal of ending WordPress's dominance of the mom-and-pop blog space.

When most people think of WordPress blogs, if they're thinking about their options at all, they're usually accepting the need for convoluted php-based mixins and cumbersome database set-ups because they had heard that WordPress is fully theme-able and customize-able. The alternative to WP themes must be something awful if so many are drawn to a de facto behemoth installation like WP.

With just a little bit of syntax help, though, Jekyll is much easier to customize at almost every level. To get started, all you need is an index.html file in the root of the blog directory. Here's mine:

---
layout: preview
---

{% for post in site.posts limit: 10 %}
  <br>
    <a href="/blog{{ post.url }}"><p>{{ post.title }}</p></a>
    <p>{{ post.date | date_to_long_string }}</p>
    <p>
      {{ post.excerpt | strip_html }}
      <br>
      <a href="/blog{{ post.url }}"> [read more...]</a>
    </p>
    <br>
    <br>
{% endfor %}

Perhaps not your typical root file, but it makes a bit of sense with some explanation. First is the stuff at the head of the document, written in YAML ('Yet Another Markup Language') format of key-value pairs of data. This sort of markup is required at the head of every rendered document, and it helps Jekyll parse through lots of important data before it gets to the actual content. There can be data about base urls, authors, dates, scheduled publishing dates... a whole host of things. For this base file, though, we only need to call out the template that will apply.

Layouts are called out by name in the YAML, and saved in a separate directory called _layouts. Remember, directories with an underscore in their name aren't rendered into the final site as their own directory, but are rather just used as mixins or resources for other parts of a template. I primarily use two different layouts: preview for the front page/index view, and post for individual posts. Here's what my layout file (preview.html) might look like:

<!DOCTYPE html>
<html lang="en-us">

{% include head.html %}

<body>
    <div style="background-color: #EEEEEE;">
    {% include nav.html %}
    </div>
    <div id="content-wrapper">
        <div id="content">
            <ul id="resume-links">
                <li><a href="http://alexpear.com/blog/archive">archive</a></li>
                <li><a href="http://alexpear.com/blog/feed.xml">feed</a></li>
            </ul>
            {{ content }}
        </div>
    </div>
    {% include foot.html %}
</body>

Now we're starting to see something that looks like a real HTML document. These layouts are where the majority of your HTML will reside. You can also make these layouts even more abstract by saving chunks of HTML as mixins in an _includes directory. These files are included using the bracket-percentage notation of {% include [file.ext] %}. These are very useful for things like header content that you know will be a part of every page (and my real preview template both the head content and navigation bars are abstracted away into separate mixins like this). The {{ content }} is generated from the body of the HTML document that calls this layout as a template, whether it's entirely generated (like posts) or entirely pre-constructed using HTML (like my index.html file).

Inside index.html we can see that there are regular HTML elements wrapping the content that's pulled in from other sources. The difference, though, is that we're pulling in an unknown number of posts from a variety of sources, so it's not quite so simple as copy->pasting some content into a block of HTML. Let's walk through the body of this doc to see what's going on to show us an indexed view of posts.

First, we have this thing: {% for post in site.posts limit:10 %}. Those of you with a bit of programming background will recognize the 'for x in y' structure of a for-in loop for iterating over Objects, and that's exactly what this is. In this case, though, post is defined as a markdown document in the _posts directory, and site.posts is the collection of all of those documents. So this same set of information will apply to each individual (and properly-formatted) document in the _posts folder.

You'll then notice that we can pull in metadata from each post using the double-bracket notation of Liquid Markup. It looks a bit like handlebars.js, and it does a lot of the same stuff, just using Ruby instead of JavaScript. Normally I'd opt for the JS option, but Liquid comes packaged with Jekyll, so we go with Liquid in this case.

Each bit of supra-index.html data comes packaged in a form of Object notation, where for each Jekyll-generated post Object, we can access YAML-specified or _config.yml-generated properties using dot-notation. Common ones include post.url, post.title, and post.excerpt.

You can also apply modifiers to some of these properties to change the way that Liquid renders them. So {{ post.date | date_to_long_string }} goes from a cumbersome sequence of numbers to 'February 3, 2015' (for example).

While some of these properties are auto-generated (like the url, which is generated from the name of each post file), many are explicitly declared in the YAML front matter of individual posts. Just like the index.html template above, each post will have its own front matter contained between two sets of triple-hyphens. The YAML for this post, for example, looks like this:

---
layout: posts
title: Blog Tech 2
author: Alex
date: 2015-02-03
published: yes
---

These key-value pairs should make sense when thinking about how we've applied properties to a post object for each post in the blog directory. Pretty neat, isn't it?

Hopefully the details have begun to make a bit more sense. The final step, then is to take a higher-level view at that ever-important directory structure that I've been harping about over the last two Blog Tech posts. While there are some variations on this directory structure theme that you can explore in the Jekyll Documentation, mine works pretty well for my purposes. Here's a quick layout of the whole tree:

/blog/
|
|--_drafts/
|   |-*.md files in post format (unpublished)
|
|--_includes/
|   |-*.html files for mixing into templates
|
|--_layouts/
|   |-posts.html
|   |-preview.html
|
|--_posts/
|   |-*.md files in post format (unpublished)
|
|--_site/ (compiled and baked site)
|   |--2014/
|   |   |-sub directories organized by post date (for 2014)
|   |
|   |--2015/
|   |   |-sub directories organized by post date (for 2015)
|   |
|   |-index.html (fully baked home page)
|
|-_config.yml (configuration file)
|-index.html (root template for landing page content)

And there you have it. In the next blog tech post, I'll talk about how I integrate this system with my current web development workflow. Thanks for reading!