Jekyll pro tip: how to design an awesome Archive page.

(TL;DR) I explain how I designed my dynamic Archive page using Jekyll+Liquid syntax logic and a bit of CSS. (Jump to the demo bit)

Today, I wanted to sort my posts into categories, so as to have a more organised weblog overall.
Jekyll handles categories pretty well actually. One has to add a category: foo line (foo being a category name) in the YAML head of a post, and that’s it! (use categories: [foo1,foo2] if you set more than one category to a post).

The difficult part is to setup a page that would allow the user to filter posts by categories.
I did stumble upon some Jekyll plugins that can automatically generate category indexes, which is quite practical indeed. I did not want, however, to use any plugin. That is why I had to try do something with liquid tags only. Also, the idea was to have only one page do the work.


The liquid logic

We have main 3 objectives:

You get the idea: we have navigation links to switch between a entire feed of your posts or between your categories ; and below them is displayed the chosen feed or category.
This way, the user can easily navigate through all your posts and their categories without having to deal with multiples pages.

1
2
3
4
5
6
<nav>
    <a href="#allposts">All posts</a>
    {% for category in site.categories %}
        <a href="#{{ category | first | remove:' ' }}"><strong>{{ category | first }}</strong></a> {% if forloop.last %}.{% else %}, {% endif %}
    {% endfor %}
</nav>

Line 2, we include a static link pointing to #allposts. When clicked, it will display the entire, unfiltered, list of posts.
Lines 3 to 5, We’re using a loop to get all categories, and for each category create a link.

Each link points to #{{ category | first | remove:' ' }}. Those parameters passed to {{ category }} help creating an anchor-proof string from the category name.
For example, for a category named “My Travels around the world”, the above will output : #mytravelsaroundtheworld.
Note: We will use the same liquid tag to define the bloc IDs to which the links we just created point to.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="catbloc" id="allposts">
    <h2>All posts</h2>
    
        <ul>
            {% for post in site.posts %}
                <li>
                    <a href="{{ post.url }}">
                    <time>{{ post.date | date: "%-d %B %Y" }}</time>
                    {{ post.title }}
                    </a>
                </li>
            {% endfor %}
        </ul>
</div>

This is no rocket science. If you use Jekyll, you’ve probably done something like that to display a chronological list of all your posts.

What is important here, Line 1, is to surround the usual loop with a div whose ID is allposts (Remember we set a link pointing to #allposts in the navigation section).
The class catbloc is quite important too. It must be the same for this bloc and all of the category blocs, which we will deal with right below.

Now, for each category, we will list the posts it contains (the loop used Line 1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{% for category in site.categories %}
    <div class="catbloc" id="{{ category | first | remove:' ' }}">
        <h2>{{ category | first }}</h2>
        
        <ul>
           {% for posts in category %}
             {% for post in posts %}
               {% if post.url %}
                 <li>
                   <a href="{{ post.url }}">
                     <time>{{ post.date | date: "%-d %B %Y" }}</time>
                     {{ post.title }}
                   </a>
                 </li>
               {% endif %}
             {% endfor %}
           {% endfor %}
        </ul>
    </div>
{% endfor %}

Note, on Line 2, we’re setting the id of the bloc to our same anchor-proof string made from the category name (without the #, this time, of course).

The CSS switching mechanism

We only want our navigation section to be permanently on screen. We want the other sections to appear only if we click the link pointing to them.
I will be using here the :target CSS pseudo-class.

.catbloc:not(:target) {
    display: none;
}

That’s… basically it.
The situation described here is that any element whose class is set to catbloc, and that is not being targeted by any anchor, won’t be displayed.

In other words, to make a section appear, the user has to click the link that points to its anchor.

Done

I like this layout a lot. I think this is both great functionality and great UX at a really low cost. Feel free to tell me what you think.

Niléane