DiversIT Europe
Previous Home Next Sep 9, 2017 Creating an archive page for Jekyll Tags: jekyll

For my Jekyll-based blog I wanted to add an archive page with an overview of all posts. Looking at some of the available archive plugins, it should not be that difficult to create something myself.

If you look at my archive page you see my archive page has separate sections for posts, articles and video’s and presentations. Every section is similar. The only difference is the posts that are selected. In my ‘posts’ collection, I use ‘categories’ to create sub-collections for certain posts. So I can use ‘categories’ to filter out certain posts to be displayed.

The templates

The Front Matter of a post entry looks like this:

---
layout: post
title: <title>
date: '2017-09-09T14:45:00.000+02:00'
author: <name>
tags:
- tag1
- tag2
categories: post
---

The archive template is:

---
layout: default
---

<a name="posts"></a>
<archieve>
  <name>Posts</name>
  {%- assign posts = site.posts | where:"categories","post" -%}
  {%- assign groupedByYear = posts | group_by_exp:"post","post.date | date:'%Y' " -%}
  <!-- total posts: {{ posts.size }} -->

  {%- for yearitem in groupedByYear -%}
  <yearlist>
    <year>{{ yearitem.name }}</year>

    <list>
    {%- for item in yearitem.items -%}
      <item>
        <date>{{ item.date | date:'%B %e'}}</date>
        <span><a href="{{ item.url }}">{{ item.title }}</a></span>
      </item>
    {% endfor %}
    </list>
  </yearlist>
  {% endfor %}
</archieve>

How the template works

To display an archive of only the items in the ‘post’ category of the ‘posts’ collection:
First the post-items to be displayed are filtered out of the posts collection.

{%- assign posts = site.posts | where:"categories","post" -%}

Then the posts are grouped by year using the ‘date’ attribute in the post’s Front Matter. From the date only the year (%Y) is used to group the items per year. The items are already in the correct order.

{%- assign groupedByYear = posts | group_by_exp:"post","post.date | date:'%Y' " -%}

The value of groupedByYear is an array of objects. Each object has a name property containing the year, an items property with the posts items of that year, and a size property with the number of items for that year.

[{"name"=>"2017", "items"=>[#, #, #, #],"size"=>4}
{"name"=>"2016", "items"=>[#, #, #, #],"size"=>4}
{"name"=>"2015", "items"=>[#, #, #, #], "size"=>4}]

Next is to loop over each grouped year value

{%- for yearitem in groupedByYear -%}
<yearlist>
  <year>{{ yearitem.name }}</year>
  ...
</yearlist>
{% endfor %}

Within this loop, loop over all items for that year. The item is just a normal post entry so all properties of a post, as defined in the Front Matter, are available.

<list>
{%- for item in yearitem.items -%}
  <item>
    <date>{{ item.date | date:'%B %e'}}</date>
    <span><a href="{{ item.url }}">{{ item.title }}</a></span>
  </item>
{% endfor %}
</list>

Grouping posts by month

Besides grouping the posts by year, it’s also possible to create a sub-grouping by month.

To group the posts by month, extract the month from the post.date. The years are naturally ordered correctly. However, to order the months correctly, the month’s number should be used instead of the name. By getting both the month’s number and name from the post.date the months are ordered correctly and the month’s name is available for output.

{% assign groupedByMonth = yearitem.items | group_by_exp:"post","post.date | date:'%m %B'" | sort:'name' | reverse %}

The groupedByMonth is an array similar to groupedByYear and by looping over the array, the items of the month can be displayed. To get the name of the month, the monthitem.name has to be split on a space, see date format above. The resulting zero-based array contains the month number and the month name. To show the newest items on top, the sorted months have to be reversed.

{%- for monthitem in groupedByMonth -%}
  {%- assign month = monthitem.name | split:" " %}
  <p>
    <h2>{{ month[1] }}</h2>
    {%- for item in monthitem.items -%}
      <h3>{{ item.date | date:'%e'}}</h3> <a href="{{ item.url }}">{{ item.title }}</a><br/>
    {% endfor %}
  </p>
{% endfor %}

Combining categories in a single list

Unfortunately, there is no filter available for selecting multiple categories from a list. To achieve the wanted result, first the wanted entries in the wanted categories have to be filtered out first.

{%- assign presentations = site.posts where:”categories”,”presentation” -%}
{%- assign videos = site.posts where:”categories”,”video” -%}

Then the arrays can be combined using the concat filter.

{%- assign posts = presentations concat:videos sort:’date’ reverse -%}

This approach is less efficient off course, but since the site is generated statically, it is not a concern. Only generating the site might take a bit longer.
To see where Jekyll spends it’s time, use the –profile option when running a build.

jekyll build --profile

Styling the archive

Here is my SASS CSS for styling the archive. I’m using a CSS Grid since CSS Grid is awesome!

/* Styling archieve page */
archieve {
  display: block;
  padding: 10px;

  name {
    font-family: "Verdana, Geneva, sans-serif";
    color: $colorPink;
    font-weight: 600;
    font-size: 22px;
  }
  yearlist {
    display: block;
    padding: 10px;
    year {
      font-size: 20px;
      font-weight: 600;
      color: $colorDarkBlue;
    }
    list {
      display: grid;
      grid-template-columns: 1fr;
      padding: 10px;

      item {
        display: grid;
        grid-template-columns: 30% 70%;
        grid-template-areas:
          "date title";
        padding-top: 3px;
        padding-bottom: 3px;
        border-bottom: 1px solid rgba(0,0,0,0.3);
        color: $colorLightBlue;

        date {
          grid-area: date;
          opacity: 0.7;
        }
        span {
          grid-area: title;

          a {
            text-decoration: none;
          }
        }
      }
    }
  }
}