As mentioned in my post about how I set up this blog, there still seemed like there might be some necessary features to be added. And as I experiment and write up some posts in advance, I can see that at least a couple of those features need to be there before the new year hits. So, this is that story.


As I add pages for future release, it became obvious that pagination isn’t a default part of Jekyll and, if I imagine two posts per week (I’ll post details on my plans next week), the blog is likely to become unusable after a few months.

A quick web search turned up Jekyll’s own page on the subject. I followed the instructions, though hit a couple of small snags:

  • The documentation isn’t quite clear that the blog’s existing index.markdown needs to be removed and replaced with an index.html file in Jekyll’s root folder. From there, the sample page makes a good start that I was able to adapt with the changes I had already made for my _layouts/home.html. Except…
  • Unlike layouts/home.html and index.markdown, the index.html frontmatter requires a title. Without it, Jekyll picks another page in the root, specifically my code of conduct, for some reason. It took a surprisingly long time to figure what was going on, there.
  • It looks like the pagination gem doesn’t understand how to combine the baseurl and paginate_path options. The link second page link pointed to /blog/page2, but the page was generated at /blog/blog/page2.
  • Because the post pages are pre-generated, they showed up in my header. I needed to add and my_page.title != 'Posts' to the _includes/header.html line that decides whether to render a link to the page.

Obviously, most of these can be worked around, but that third item was a huge problem that didn’t appear to have any resolution. So, I started to investigate the successor project.

I updated the gem and then stumbled around, trying to find the configuration documentation. It claims that it’s compatible with the original version, but actually requires a completely different configuration. Eventually, I found the directions and was able to quickly remove the old configuration and replace it with…

  enabled: true
  per_page: 8
  permalink: 'page/:num/'
  sort_reverse: true;

It seems silly to require enabling it as part of the configuration, and sillier still to make reverse-chronological sorting an option when it’s obviously how every blog works, but sure. Oh, also silly (keep scrolling down the documentation page), the index file needs…

  enabled: true

…added to the index.html frontmatter, because we haven’t turned this feature on nearly enough times, I guess.

If you scroll even further down the page, you can see the warning stating that backward-compatibility was dropped in January 2018, explaining that mystery.

Just one problem, this time, despite the goofy configuration. Once again, the permalink setting doesn’t play well with baseurl. In this case, though, I just added /blog to the start of the paging URLs in the index. There’s probably a better way of handling this, but it does the job.


Jekyll has some gems to handle tags, but on the chance that I might eventually want to use GitHub Pages to host the blog, it looks like the state of the art is Long Qian’s approach.

It took about five minutes to churn through the entire thing, though there are a few minor glitches.

First, the instructions aren’t using siteurl, which means tweaking some of the tag-page URLs to make this work.

Second, someone needs to keep the tag pages up to date as posts are added and modified. Fortunately, I already have a script I use to rebuild and deploy the blog to my server, so it’s easy enough to add something like…

rm tag/*.md
for tag in $(grep '^tags:' _posts/* | cut -f2- -d'[' | cut -f1 -d']' | tr -d ' ' | tr ',' '\n' | sort -u)
  cat > "tag/${tag}.md" <<EOF
layout: tagpage
title: "Tag: ${tag}"
tag: ${tag}

Now, every time I rebuild the blog, the tags will also get rebuilt. If you’re curious and aren’t familiar enough with the Linux command line to work it out on sight, the above code removes the existing tag files and (a) checks all posts for tags, (b–e) sets them up one per line, and then (f) makes sure that each only appears once. Then, for each unique tag, I create a tag file named after the tag with a layout, title, and tag name.

Third is another mild frustration: Since the tag files are not posts, Jekyll’s default wants to include them in the blog header’s links. I filter them out the same way I filter the pagination files, above.

Because tags are represented in Jekyll by non-post pages, it’s also easy to iterate through the pages and link to each tag page in hopes of helping readers find posts of interest. That code…it turns out that Liquid templates are processed before converting from Markdown, so I can’t show it in full. So, the following replaces the open braces ({) with open parentheses (() to not just dump the tags into this page.

(%- assign default_paths = site.pages | map: "path" -%}
(%- assign page_paths = site.header_pages | default: default_paths -%}
(%- for path in page_paths -%}
  (%- assign my_page = site.pages | where: "path", path | first -%}
  (%- if my_page.title -%}
    (%- if my_page.title -%}
      (%- assign title = my_page.title | split: " " -%}
    (%- endif -%}
    (%- if title[0] == 'Tag:' -%}
        <a href="/blog/tag/(( title[1] }}">
          <i class="fa fa-tag"></i>
          (( title[1] }}
    (%- endif -%}
  (%- endif -%}
(%- endfor -%}

And that’s tagging taken care of.

The tags will probably go a long way towards making posts discoverable, but as the number of pages increases, I’m going to want the ability to search for a page. I mean, I don’t need a search box, because I can just search the source files to find what I want. But someone might need to search it who doesn’t want to go to that kind of trouble.

Fortunately, Sharath at Webjeda found a reasonable solution. I needed to play with URL paths again and tweak the Liquid template to prevent tab characters from fouling up the JSON, for some reason, but I now have a working search page that works as advertised!

Putting the search bar on the header might make more sense, at some point, but loading up all the text of the blog onto every page honestly seems like a performance nightmare, so I probably won’t bother until I see complaints about the separate search page.

In the future, I may want to update the search code to strip out diacritical marks, so readers don’t need to worry about it. But for now, there are few enough non-Latin characters for me to worry about it just yet. Unfortunately, it doesn’t look like it’s possible to do that using Liquid’s templates that generate the JSON, so it’ll need to happen in the JavaScript code.

The Winners, Take Two

I still have a couple of tasks ahead of me for this to seem to be at one-hundred percent:

  • Update search to ignore diacritical marks.
  • I would like Hashover to use Libravatar instead of Gravatar; while I trust Automattic more than I trust Disqus, it’s still relying on a company that might start selling data.
  • The jury is still out on anonymous comments.

However, for now, the tools that work, in addition to Jekyll and Hashover, are:

And I’ve taken a quick look at Libravatar’s API and it seems entirely plausible if Hashover’s architecture will allow another service.

So, with that, I think the blog is pretty much ready for the new year. What a nice Christmas present for myself! 🎁

Credits: Untitled header photograph from PxHere, made available under the CC0 1.0 Universal Public Domain Dedication.