Jekyll is great. It generates a web-site based on templates, meaning that you edit templates to change things and generate static pages to serve then. Here are my notes on how to use it.

Installing

Installing jekyll on Debian is as easy as apt-get install jekyll. As Jekyll wants to process templates, you then need such templates. If you are starting for the first time, it might be a good idea to have Jekyll generate you a new site, which is easy too:

user@host:~$ jekyll new test
New jekyll site installed in /home/user/test.

The created folder contains several files and folders, which together make up a site after being processes by Jekyll. So let us have a look at those:

  • css/ - a folder to contain all your CSS files.
  • _includes/ - a folder with files generating code to be included in other files being generated.
  • _layouts/ - a folder with files specifying how certain content should look like.
  • _posts/ - a folder for blog-style postings, including a demo post
  • about.md - a demo about-page using markdown
  • _config.yml - the one major configuration file for Jekyll
  • feed.xml - an example on how to generate a XML feed
  • index.html - an example for an entry page generating an overview of available content

Note the _ of some of the folders. Those mark special treatment by Jekyll. The _config.yml contains central settings for the whole site. Have a look at it with your favorite editor. It should look like this:

# Site settings
title: Your awesome title
email: your-email@domain.com
description: "Write an awesome description for your new site here. You can edit this line in _config.yml. It will appear in your document head meta (for Google search results) and in your feed.xml site description."
baseurl: ""
url: "http://yourdomain.com"
twitter_username: jekyllrb
github_username:  jekyll

# Build settings
markdown: kramdown
permalink: pretty

You probably want to change title, email, description to your liking. The baseurl and url are worth a little more explaining. they will be used in the generation of links for your site, like url + baseurl + folder + page. So when you start generating your pages, wrong settings in these might lead to broken links. The usernames are examples that are used in the generated footer only, ignore them for now. Same goes for the build settings.

Building the Site

Now with the templates available and probably even your own name set, you might want to run Jekyll the first time to build your site. If you haven’t done so yet, go into the folder and call Jekyll with the subcommand build there:

user@host:~$ cd test
user@host:~/test$ jekyll build
Configuration file: /home/user/test/_config.yml
            Source: /home/user/test
       Destination: /home/user/test/_site
      Generating... 
                    done.

Jekyll has analysed all files in the directory and generated your site into the subfolder _site. Open the index.html file from _site in your browser to view your site now. You can do so by just dragging and dropping the file onto your browser if you cannot find the dialogue to open local files. It will use an URL like file:///home/user/test/_site/index.html in the browser.

Like what you see? It means you are ready to go, in a technical sense. You only need to provide the content now, to get your site going. If you are working on the site and want to regularly rebuild it, you can use the switch --watch. This will cause the command to keep running and automatically re-build the site if any of the content has changed. But be warned that changes to _config.yml will not be recognised by those rebuilds, so if you change anything there, then you need to stop the running Jekyll and restart it.

Understanding the Build

Let’s have a look what Jekyll is doing here to understand the thing a bit. The index.html you are probably looking at has been generated, and I wanted to understand how. If looking at its source, i.e. test/index.html in the opposite to the generated test/_site/index.html, then we see it starts with some kind of special code:

---
layout: default
---

This is what’s called a front matter and always consists of the three hyphens at the beginning and the end. Between those there can be any number of variables declared, here it is only one.

Note: Only files with such a front matter are processed at all, so remember that.

The variable layout defines for it’s content, what file from _layouts is going to be used to generate it. So for this index.html we need to look at _layouts/default.html. This file contains the following:

<!DOCTYPE html>
<html>
  {% include head.html %}
    <body>
    {% include header.html %}
    <div class="page-content">
      <div class="wrap">
      {{ content }}
      </div>
    </div>
    {% include footer.html %}
    </body>
</html>

This is a mixture of HTML tags (indicated by the < and >) and commands for Jekyll (indicated by the {%
and %} ). Here, the code is putting the basic HTML tags into the resulting document and including (importing) content from head.html, header.html, and footer.html as well as the variable content. Let’s look at the files first. Those are all in the folder _includes, which is where the include command will look for them. These are pretty important as they allow you to change the content of all pages. Here is what they do:

  • head.html specifies the head section to be used in all generated documents containing meta-data.
  • header.html specifies a front part (by default the upper menu-bar) for all generated docuemnts.
  • footer.html specified a bottom part (by default the footer with email, link to github and the site’s description) to all generated documents.

The variable content mentioned before does nothing more than add the content of your page, in this case the index.html with which we started above. Until now we only look at the front matter (header) of it. The rest of this index.html looks like this:

<div class="home">
  <h1>Posts</h1>
  <ul class="posts">
    {% for post in site.posts %}
      <li>
        <span class="post-date">{{ post.date | date: "%b %-d, %Y" }}</span>
        <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
      </li>
    {% endfor %}
  </ul>
  <p class="rss-subscribe">subscribe <a href="{{ "/feed.xml" | prepend: site.baseurl }}">via RSS</a></p>
</div>

Again a mixture of HTML and commands for Jekyll. This time, only a middle part is defined. Here it is contained in a div-element. The commands in it iterate over all posts of the site (more on what that means - it’s not all of the site - later) and generates list-items for them. That basically means that if you add posts to your site, the generated index.html will automatically contain your new posts after the next build.

Adding Content

Adding content to your site is as easy as a creating a file and putting a front matter into it. You can use other formats besides HTML, I use markdown mostly. It’s much easier to edit. And you you can mix in HTML any time you need something more complex than markdown allows.

However, Jekyll treats different kinds of content differently, so its worth to learn about these differences first. You often want to have different kind of content on your site in the sense of different treatment, so this is really cool. But it adds complexity and needs to be understood. Basically Jekyll knows about these different kinds of content in the beginning:

  • Posts

    Only content with a front matter within folder _posts will be seen as posts if they adhere to a naming convention composed of date, name, and extension (yyyy-mm-dd-name.ext). For example the automatically generated 2015-10-26-welcome-to-jekyll.markdown is a valid filename to put in _posts. Posts are the only content shown by the index.html discussed above.

  • Pages

    All other files with a front matter will be treated as pages. These are not included in the list generated by the index.html as discussed above. Instead there is a similar loop as in that index.html in the _includes/header.html which will generate a list of all pages and as those as links to the header bar. Quite important to know …

  • Static Content

    Everything not recognised as one of the above. These are not processed by Jekyll and instead are simply copied to the destination directory. This is how you want images and alike treated.

So let’s add content of each type as a training. Create a file in _posts with a valid name like 2015-10-26-test.md and put some simple content in like this:

---
layout: post
title:  "test-post"
date:   2015-10-26 15:49:50
---
A test posting into my new blog.

If you generate your site (see above) you will notice the front page displaying your page. To add a page create a file named test.html in base directory and put some simple content in there like this:

---
layout: page
title:  "test-page"
date:   2015-10-26 15:53:11
---
A test page for my new blog.

If you generate your site again (see above) you will notice that now the top bar has changed and contains the name of your new page as a link. Any static file will just be copied over, try to add a favicon to be displayed by the browsers to your base directory for example.

Styling and Organising Content

You know how to add content now. Let’s have a look at styling and ways of organising content, so you get a picture of the possibilities.

Styles

The above generates HTML code that is styled by the main.css in the css folder. The inclusion is defined in the header in _includes/head.html and thus gets used by all files. So to change styling, just open the css in your editor and add/change values. You can of course use different ids in the HTML tags and add styling information for those, too.

You can organise content by just putting files into folders without a _ in beginning of the foldername. However, the automatically generated menue listing all pages (but posts) will get annoying soon. It’s just for few pages, not for masses of such. To change what gets into the header, you can change the _includes/header.html file and adjust it to your needs. You can of course also remove the code that generates the entries and replace it with a static menue.

So this is easy if you know css. As a hint if you are new to this, many browser have debugging functionality build into them these days. In Firefox just right-click and use “inspect element”, and you will get a windows that shows you source code of the page as well as styles that got associated. You can even change values there temporarily to check out new values. Afterwards you just change the css file and enjoy the new look of your site.

Tags

Tags were something I stumbled upon early and had to deal with until lately.

Assigning Tags

You can easily assign tags to your content by including them in the front matter like this:

tags:
- i2p
- jekyll

This would add the tags i2p and jekyll to the page. Thing is, that at first nobody tells you what to ever do with those tags. Turns out its not that complicated though. Tags simply get added to the data-structures and can be accessed there.

Overview of Tags

I recently found out as described in the blog-post. So after assigning the tags, I wanted a page to display a list of tags and the blog-posts about it. I created a page tags.html in my base directory and put this into it:

---
layout: page
title: Tags
author: little big t
---
This is the alphabetically sorted list of tags that I have used so far. 
They list all postings with that tag.
<div class="categories">
  <ul>
    {% capture tags %}
      {% for tag in site.tags %}
        {{ tag[0] }}
      {% endfor %}
    {% endcapture %}
    
    {% assign sortedtags = tags | split:' ' | sort %}
    
    {% for tag in sortedtags %}
      <h3 id="{{ tag }}">{{ tag }}</h3>
      <ul class="content-list">
        {% for post in site.tags[tag] %}
          <li><a href="{{ post.url }}">{{ post.title }}</a></li>
        {% endfor %}
      </ul>
    {% endfor %}
  </ul>
</div>

Again the mixture of front matter (to get the file processed) and HTML tags + escaped Jekyll code to generated content. What the code does is to generate an array with all tags, sort it, and then iterate over that array and generate the tag name and links to all posts with that tag for each tag. This is how the tag-page is generated.

Tags on Posts

For blog posts, where I mostly use the ags, I then wanted to show what tags had been associated with them. For this I will need to change the layout used for posts, _layout/post.html. I first fiddled around with a structure similar to the one explained above for the overview. But it turned out to be much simipler, in the end. Here is what I added to the bottom of that layout:

<p>Tagged: {{ page.tags | array_to_sentence_string }}</p>

Instead of manually iterating over the tags, I here used the function array_to_sentence_string. This will take the list and output it with comma and and as appropriate. So for two items in the list it will output “1 and 2”, while for three “1, 2, and 3” and so on.

Categories

Categories seem to be a bit like tags. But I think I have seen folders being created for categories, so there is probably more to them. Haven’t found out yet. You can, however, treat them like tags and generate a page with all collections as shown above for tags. Will update this once I know more …

Collections

To organise content there is an experimental feature called collections. These basically allow you to create groups of content like posts and pages of your own types. It is currently not advised to use this feature on production systems, but let’s have a look. I’m using this here for the howtos, so let’s take them as an example. I extended the _config.yml with the following:

collections:
  howto:
    output: true

This defines a collection howto and sets its output-variable to true. Before explaining the latter, here’s how collections currently work. The folder _howto (or whatever name you gave your collection) is being reserved for this collection now. You put files into it, to add them to the collection. Only with the output-variable set as above will Jekyll process these files and generated according output-files. Without it, you can still process the data contained in those files but need to generate any output for yourself.

Now for the howtos, this document is saved as jekyll.md in my _howto folder. As the items of the collection or not getting linked automatically, I then further created a howto.html in my base directory. As such it will be a page and will be automatically shown in the header (if you don’t change what’s shown there as mentioned above). It will also get copied into the folder howto in the target directory. Now I only needed it to show all howtos. Here’s how I achieved that:

---
layout: page
title: T's How-To Section
author: little big t
---
<div class="home">
  <h1>How-To Content</h1>
  <ul class="content-list">
    {% for howto in site.howto %}
      <li>
        <a class="post-link" href="{{ howto.url | prepend: site.baseurl }}">{{ howto.title }}</a>
      </li>
    {% endfor %}
  </ul>
</div>

This is how currently the How-To-overview is generated.

Pagination

With the rising number of blog-posts the main page got worse to handle. As it was so far showing all blog-posts, scrolling to the bottom became like … work ;) I looked into pagination therefore and turns out this already build in. Adding paginate: 5 will prepare data-structures, but one needs to use them.

It’s rather easy though. Instead of iterating over all posts like in the original for post in site.posts, you iterate over the objects of the created paginator with for post in paginator.posts. That’s almost it, the main content already gets displayed correctly. What’s needed in addition is a page navigation.

  <!-- Pagination links -->
  <div class="pagination">
    {% if paginator.previous_page %}
      {% if paginator.previous_page == 1 %}
        <a href="{{ paginator.previous_page_path }}" class="previous">Previous</a>
          {% else %}
        <a href="{{ paginator.previous_page_path }}/" class="previous">Previous</a>
      {% endif %}
    {% else %}
      <span class="previous">Previous</span>
    {% endif %}
    <span class="page_number ">Page: {{ paginator.page }} of {{ paginator.total_pages }}</span>
    {% if paginator.next_page %}
      <a href="{{ paginator.next_page_path }}/" class="next">Next</a>
    {% else %}
      <span class="next ">Next</span>
    {% endif %}
  </div>

This is a slightly modified version of the example code from the docs. I added the distinction for paginator.previous_page == 1 because it pointed at http:/// otherwise for the first page after I added the / after the generated links. That’s need for the index.html to in those folders to actually load.

Deploy: Publishing and Running Your Site on I2P

Running your site basically just means to copy the generated files to a web-server. But wait, why not generate them where you want them? Jekyll offers an option to specify the output-path, so if you are on I2P and want to publish your site, make sure:

  • you have updated the sites url in _config.yml to your b32 address of the tunnel
  • your user has the needed rights to write into the docroot directory

and then run:

jekyll build --destination /var/lib/i2p/i2p-config/eepsite/docroot/

You can use the --watch switch here, too. But remember that it will not recognise changes to your _config.yml (see below).

Hints & Pitfalls

Here some pitfalls I ran into and how to work around them and hints on how to achieve common things quickly …

Icon (favicon)

To have browsers display an icon for your site, include favicon.ico in your base directory (it will just get copied over) and include the following line in your _includes/head.html:

<link rel="favicon" href="/favicon.ico" />

Note that your browser might not recognise the new icon immediately. I guess it’s cached somewhere deeper within Firefox (in this case) then … Try with another browser if it should work but just doesn’t seem to …

Variables and Watch-Mode

I ran into a problem with changing the email-address displayed in the footer. There is a little note on the page explaining variables that explained my problem:

Jekyll does not parse changes to _config.yml in watch mode, you must restart Jekyll to see changes to variables.

So note that if you use --watch you will do a restart if changing variables in _config.yml. I stumpled upon this with changing the email as well as introducing collections.

ClearNet links used:

  • http://jekyllrb.com/docs
  • http://jekyllrb.com/docs/pagination/