Advertisement

Building Static Sites with Jekyll

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →

A full-blown CMS is rarely necessary. Sometimes, you only need to create a light, static website … but you have just enough pages to make the process of copying template files and making cross-site markup changes a chore. Today, I’ll demonstrate a simple solution—Jekyll—that will make creating small websites a breeze.


Step 0: Meeting Jekyll

Jekyll is a simple, blog aware, static site generator.

Jekyll is a simple, blog aware, static site generator. That’s what the site says. But, what exactly does this mean? A static site generator is a program that takes a set of files and generates your site with them. As you’ll see, we’ll be able to use a set of templates, create the content files separately, and then use Jekyll to generate our site. The “blog aware” part means that we could use this to create a blog, or any website that has a series of post-like entries (such as a portfolio). Let’s give it a try!


Step 1: Installing Jekyll

Refer here for more information on Ruby Gems.

We’ll begin by installing Jekyll; it’s a Ruby gem, so doing so should be pretty straightforward.

gem install jekyll # use `sudo` if your setup requires it

Yep: it’s that easy. There are a few more pieces we could install if we are planning on doing a more complex set-up, however, since we’re not, this will do the trick.


Step 2: Creating our First Template

Every file or folder that does not begin with an underscore will be copied to the generated site.

Next, let’s set up the folders for Jekyll. Create a folder, called example-app for this tutorial; we’ll be creating a little portfolio site for, say, a photographer. This is a great example of where Jekyll shines: it’s a small site that won’t be updated too frequently, but is large enough to the point that you don’t want to open every page when you need to make a markup change.

Inside example-app, create a folder called _layouts. Notice the underscore at the beginning of this folder: any folder or file that begin with an underscore will not be part of the site that Jekyll generates. If they have a name that Jekyll recognizes (such as _config.yml or _layouts), their contents will be used in the generation of the site, but the files themselves won’t show up in the site. Remember this: every file or folder that does not begin with an underscore will be copied to the generated site (which, by the way, defaults to the _site sub-folder).

So, let’s create a layout. We’ll start with a general site layout that includes all the “chrome” for our site. Create a new file, called default.html inside the _layouts folder (the name doesn’t matter), and add the following code to it:

<!DOCTYPE html>
<html>
<head>
  <meta charset=utf-8 />
  <title> {% if page.title %} {{ page.title }} | {% endif %} John Doe, Photographer </title>
  <link rel="stylesheet" href="/css/styles.css" />
</head>
<body>

  <div id="main">

    <header>
      <h1> John Doe Photograghy </h1>
    <header>

    <nav role="navigation">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/portfolio/">Portfolio</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>

    {{ content }}

    <footer>
      <p>@copy; John Doe Photography 2011 | All Rights Reserved. </p>
    </footer>

  </div>
</body>
</html>

A couple of things here to keep in mind...

Firstly, Jekyll uses the Liquid template system (by default). This means, anything you can do with Liquid, you can do in a template in Jekyll. For example, in the <title> tag, we’re using both types of Liquid markup: output markup and tag markup. Output markup may output text (if the variable referenced exists), while tag markup doesn’t. Output markup is delimited by double curly-braces, while tag markup is delimited by a the curly brace / percent sign duo.

The next thing to notice above is what is inside the Liquid tags: things like page.title and content. These are variables provided by Jekyll; you can see the list of available template data in the docs. We can also create custom template data, as we’ll review shortly.

Lastly, notice the CSS we’re linking to: create a css folder in the root of your project and throw this bit of styling into a style.css file:

body {
  font: 16px/1.5 verdana, helvetica-neue, helvetica, arial, san-serif;
  background: black;
  color: #ececec;
  padding: 0;
  margin: 0;
}
ul {
  margin: 0;
  padding: 0;
}
a {
  color: #ccc;
  text-decoration: none;
}

a:hover {
  color: #ececec;
  text-decoration: underline;
}

#main {
  width: 960px;
  margin: 0 auto;
  background: rgba(255, 255, 255, 0.4);
}
header {
  padding: 0 10px;
  overflow: hidden;
}
h1 {
  margin: 0;
}

nav ul, ul.entries {
  list-style-type: none;
}
nav li a {
  float: left;
  margin: 5px; 
}
.content {
  padding: 10px;
}

ul.entries li {
  position: relative;
  margin: 20px auto;
  padding: 20px;
  background: #ececec;
  width: 600px;
}

ul.entries img {
  width: 600px;
}

ul.entries li h3 {
  position: absolute;
  bottom: -18px;
  left: 17px;
  font-size: 2em;
}
ul.entries a {
  color: #ececec;
}
ul.entries a:hover {
  color: #fff;
}

footer {
  font-size: 0.65em;
  text-align: center;
}

Also, create an img folder and add an image, named banner.jpg; we’ll be using it shortly. Any image will do; just crop it to 960px by 300px;.

You might be wondering why we're using the if statement above if the page.title variable just won’t display if it exists? Well, if it does exists, I want to include the vertical bar after it; another way to write that would be like this:

{{ page.title }}{% if page.title %} | {% endif %}

So, how we do use this template? Well, we need to create a page that will use this template. In the root directory of our project, create an index.html file. Here’s the content:

---
layout: default
---
<section role="banner">
  <img src="/img/banner.jpg" />
</section>

<section class="content">
  <p>
  Welcome to John Doe Photography! Please, check out my <a href="/portfolio/">Portfolio</a> to see my work.
  </p> 
</section>

Here’s the content of our index.html file. Notice what’s at the top of the file: Jekyll calls this YAML front matter. Any file (that doesn’t start with an underscore) that has YAML front matter will be generated by Jekyll before being put in the _site folder (if it has no underscore or YFM, then it will just be copied _site). In this case, the YAML front matter just tells Jekyll what template we want it to use.

Now, open a terminal, cd into your project directory, and run jekyll. You should see something like this:

WARNING: Could not read configuration. Using defaults (and options).
  No such file or directory - /Users/andrew/Desktop/example-app/_config.yml
Building site: /Users/andrew/Desktop/example-app -> /Users/andrew/Desktop/example-app/_site
Successfully generated site: /Users/andrew/Desktop/example-app -> /Users/andrew/Desktop/example-app/_site

Ignore the warning; we’ll come to that shortly. For now, you can see that the site has been built in a freshly-created _site directory. If you open the _site/index.html file in your browser of choice, you should see … a failure. The problem is that our paths (urls and stylesheet) begin with a forward slash. This means we can’t just view them as files, we need to view them on a server. Sure, you could go start up W/MAMP, but why take the trouble? Jekyll has a built in server. So, run jekyll --server, and go to localhost:4000 to see something like image below:

Tutorial Image

If the image above isn’t enough, look at the code of _site/index.html. You’ll see that the template we specified was blended with the content we provided and—voila!—we have our page.

I want to remind you that it’s the YAML front matter that makes this magic happen; if a file doesn’t start with three dashes, one or more lines of properties, and another line of three dashes, the file will just be copied to the _site folder, no generation taking place.


Step 3: Creating a Portfolio Template

Now that we’re comfortable with the basics, let’s create a portfolio for our fictional photographer. Remember how I noted that Jekyll is “blog aware”? Well, we’re going to use this blog-awareness feature to our advantage: instead of posts, we’ll have portfolio entries.

Posts belong in a folder, called _posts, so create that now. The file name pattern for posts must be specific as well: year-month-day-title.ext. Posts — well, any file in your Jekyll site, really — can be either Markdown or HTML.

So let’s make a few posts: remember, these will actually be entries in our portfolio:

_posts/2010-03-04-bikes.md

---
layout: portfolio_entry
image: /img/bikes.jpg
title: Bikes, Black and White
---
Bikes are used by almost everyone in downtown Amsterdam. These ones are chained to a bike rack.

_posts/2010-10-01-wing.md

---
layout: portfolio_entry
title: Wing and a Prayer
image: /img/wing.jpg
---
The wing of the AirBus I rode to England.

_posts/2011-06-05-bridge.md

---
layout: portfolio_entry
title: Stone Bridge
image: /img/bridge.jpg
---
An old stone bridge in London.

_posts/2011-07-09-road.md

---
layout: portfolio_entry
title: Road and Curb
image: /img/road.jpg
---
Bike lanes here are terribly thin.

Pretty simple, eh? Notice how we’re creating a custom YAML front matter field: image. This is the URL to the image for that entry. Sure, we could build the whole entry HTML here in this file, but what if we want to change that? We’d have to return and change it in every entry. This way, we can instead use our portfolio_entry template to render them. What’s that template look like? It’s pretty simple too:

_layouts/portfolio_entry.html

---
layout: default
---

<h2 class="content">{{page.title}}</h2>

<img src="{{ page.image }}" />

{{ content }}

If you looked at the template data page, you’ll know that any custom front matter we add will be available under page; so, here, we can access page.image. We’re also using page.title and content (everything after the last three-dash line).

I should mention here that, while the post title is supposed to be available on the post object, I’ve only been able to get it to work on the page object. Whatever works!

Also, notice that we have this template using our default layout. You can nest templates like that, and make your job even easier.

This gives us our entry (post) pages, but what about the main portfolio page? When writing our navigation in our default layout, I noted that we want it as /portfolio/. So, create a folder, called portfolio in the root directory, and open an index.html file within it.

---
layout: default
title: Portfolio
---

<section class="content">
  <h2>Portfolio</h2>

  <p>Check out my images below!</p>
</section>

<ul class="entries">
  {% for post in site.posts %}

  <li>
    <a href="{{ post.url }}">
      <img src="{{ post.image }}" />
      <h3>{{ post.title }}</h3>
    </a>
  </li>

  {% endfor %}
</ul>

This is our most complicated piece yet. Remember, this isn’t a template: it’s a “normal” file, but it can still include Liquid tags. We start by setting layout to default, and title to “Portfolio.”

Notice that, in the HTML, we have a Liquid for-in loop. We retrieve all the posts with sites.posts; then, we loop over those posts with for post in site.posts / endfor. If you’ve worked with WordPress, or any other blogging system, you should be familiar with the concept of a loop. That’s all this is! Inside, as you can see, we can get the standard properties, as well as any front matter we defined (like image).

Now if we run jekyll --server to re-generate the site and start the server, localhost:4000/portfolio/ should show this:

Tutorial Image

And here’s an entry page:

Tutorial Image

Great! You’ve created a portfolio. I’m sure you see, as well, how this works for a blog. Let’s now move on to look at some configuration options for Jekyll.


Step 4: Writing a Config File

There’s a plethora of options for Jekyll. It’s great that all of them have really sensible defaults, but if you want to change them, it’s not hard at all.

There are two ways to set options.

  • First, when you run the program on the command line, you can pass parameters. We’ve already seen the --server parameter, which starts a server after generating the site.
  • A different way, and the way we’ll use here, is in a config file, called _config.yml; this is a YAML file, so each line is a key: value pair, just like in the YAML front matter. Jekyll will look for this file before generating site.

So, make an _config.yml file, and let’s check out some of the most common options.

For a complete list of options, review the configuration documentation.

  • auto: Adding auto: true to your config file will keep Jekyll running, watching your project folder for changes and regenerating the site on the fly.
  • source: If your source files are in a different directory than the one you’re running Jekyll from, you’ll want to set that directory with the source property.
  • destination: By default, the destination for your generated site is ./_site. If you’d like something different, set it here.
  • permalink: The permalink is the path to your posts. By default, that’s /year/month/day/title.html. However, you can customize that if you want. Among others, you can use the variables :year, :month, :day, :title, and :categories. :categories comes from the front matter; all the others come from the post file name. Then, you can set permalink to things like /:year/:month/:title/ or /:categories/:title.html. Bonus tip: if you have a permalink property in the post front matter, it will override the site-wide default.
  • exclude: Like I said above, Jekyll won’t generate files in directories starting with an underscore. But, if you have folders that you want it to ignore, but that don’t start with an underscore, you can do it with exclude in your config file.

Step 5: Deploying the Site

So, let’s say you’ve created the site, and want to set it free, out on the world wide web. How do you do that?

There are several ways to accomplish this. Of course, if it’s a small site that you won’t be updating too often, then simply FTP it up to your server; this might be your only option, if you’re using shared hosting.

If you’ve got a VPS or dedicated hosting setup, you can run more automatically. Check out the deployment documentation for a list of good ideas. If you aren’t sure what to do, try following the directions for using the git post-receive hook; I’ve tried that, and it’s pretty cool.


Step 6: Taking it Further

This is just the tip of Jekyll.


Conclusion

Well, that’s your introduction to Jekyll - the simple, blog aware, static site generator. The next time you’re building a brochure-style, business-card-y, micro-portfolio site, think you’ll give Jekyll a try? Let me know in the comments and thank you so much for reading!

Advertisement