Skip to main content

Journal

Flexible tag-like functionality for custom keys in Eleventy

I have an open-source, Eleventy-based project where the posts are restaurants, each of which is located in a particular city, and contributors to the repo can add a new restaurant as a simple markdown file.

I just had to solve a conundrum wherein I wanted a custom front matter key, city, to have similar features as tags, namely:

  1. it takes arbitrary values (e.g. Glasgow, or London, or Cañon City, or anything a contributor might choose);
  2. there is a corresponding cityList collection;
  3. there is a page which lists all cities in the cityList collection as links; and
  4. there’s a page for each city which lists all restaurants in that city (much like a “Tagged Glasgow” page).

You could be forgiven for asking: why didn’t I just implement the cities as tags? I could have tagged posts with “glasgow”, or “edinburgh” for example. Well, here was my rationale:

  • for cities, I need the proper, correctly spelled, spaced and punctuated name so I can display it as a page title. A lowercased, squashed together “tag” version wouldn’t cut it;
  • I list “all tags” elsewhere and wouldn’t want the cities amongst them. Although Eleventy allows you to filter given tag values out, in this case that would be a pain to achieve because the city values are not known up front;
  • Lastly it just felt right for ease of future data manipulation that city should be a separate entity.

This task was a bit of a head-scratcher and sent me down a few blind alleys at first. Rightly or wrongly, it took me a while to realise that only all posts for tag values are automatically available as collections in Eleventy. So any other collections you need, you have to DIY. Once I worked that out, I arrived at a strategy of:

implement all the requisite functionalty on tags first, then emulate that functionality for my custom key.

First port of call was the Eleventy Tag Pages tutorial. That showed me how to use the existing “collection for each tag value” to create a page for each tag value – the “All posts tagged with X” convention. (Here’s an example on this site.)

I then referenced the eleventy-base-blog repo which takes things further by creating a page which lists “all tags”. To achieve this you first need to create a custom tagList collection, then you create a page which accesses that new collection using collections.tagList, iterates it and displays each tag as a link. Each tag link points to its corresponding “All posts tagged with X” page we created in the step above.

So now I had everything working for tags. Next step: emulate that for cities.

Here’s what I ended up doing:

Create new file _11ty/getCityList.js

module.exports = function(collection) {
  let citySet = new Set();
  collection.getAll().forEach(function(item) {
    if( "city" in item.data ) {
      let city = item.data.city;
      citySet.add(city);
    }
  });

  return [...citySet];
};

Then add the following to .eleventy.js

// Create a collection of cities
eleventyConfig.addCollection("cityList", require("./_11ty/getCityList"));

// Create "restaurants in city" collections keyed by city name
eleventyConfig.addCollection("cityCollections", function(collection) {
  let resultArrays = {};
  collection.getAll().forEach(function(item) {
    if(item.data["title"] && item.data["city"]) {
      if( !resultArrays[item.data["city"]] ) {
        resultArrays[item.data["city"]] = [];
      }
      resultArrays[item.data["city"]].push(item);
    }
  });
  return resultArrays;
});

Next, create new file cities-list.njk:

---
permalink: /cities/
layout: layouts/home.njk
---
<h1>All Cities</h1>

<ul>
{%- for city in collections.cityList -%}
  {% set cityUrl -%}/cities/{{ city }}/{% endset %}
  <li><a href="{{ cityUrl | url }}">{{ city }}</a></li>
{%- endfor -%}
</ul>

Finally, create new file posts-in-city.njk:

---
renderData:
  title: Restaurants in “{{ city }}”
pagination:
  data: collections.cityList
  size: 1
  alias: city
permalink: /cities/{{ city | slug }}/
---
<h1>Restaurants in {{ city }}</h1>

{% set postslist = collections.cityCollections[ city ] %}
{% include &#39;postslist.njk&#39; %}

And that’s a wrap! Eleventy will do the rest when it next runs, creating for each city a page which lists all restaurants in that city.

Footnote: I should acknowledge this 11ty Github issue in which Ed Horsford was trying to do something similar (create a separate tags network) leading to Zach Leatherman pitching in with how he created noteTags for his website’s Notes section. That led me to Zach’s website’s repo on Github, specifically .eleventy.js and tag-pages.njk, without which I wouldn’t have found my way.

Get Waves

I’ve been admiring the wave effect at the foot of banners on Netlify’s website and had noted that they were achieved using SVG. So this tool which helps you “make waves” is pretty timely!

In second year a nice school pal introduced me to The Cure when I was on an otherwise strict diet of rave tapes. Mixed Up with its dubby extended mixes sealed the deal. Shamefully haven’t yet seen them live so I’m at fever pitch for tomorrow’s gig at Bellahouston Park. Hopefully see some of you there!

---

Must have missed this track (from the LP The Sorrow of Derdriu) on previous listens. A Bladerunner-esque beauty!

From dynamic to static

“I’ll just make a few small tweaks to my website…” said I. Cut to three sleep-deprived days later and I’ve rebuilt it, SSG/JAMstack-stylee with Eleventy and Netlify and entirely re-coded the front-end. Silly, but so far so good, and it’s greasy fast!

So yes, I’ve just updated my website from being a dynamic, LAMP-stack affair which used Perch CMS and was hosted on Linode to being statically-generated using Eleventy and hosted on Netlify.

It mostly went smoothly. And the environmental and continuous deployment boilerplate that Netlify provide are fantastic, and will be a real time-saver over my current “set up and maintain a Linode server” approach.

In terms of challenges and troubleshooting, I did have to find a solution to the issue of FOUT on repeat visits. It seems this was happening as a result of Netlify’s interesting approach to asset caching which works well for most requirements but wasn’t so great for self-hosted webfonts. My solution was to add specific headers for .woff and .woff2 files in my application’s Netlify config file.

Saying bye-bye to autoprefixer

For a while now I’ve been using gulp-autoprefixer as part of my front-end build system. However, I’ve just removed it from my boilerplate. Here’s why.

The npm module gulp-autoprefixer takes your standard CSS then automatically parses the rules and generates any necessary vendor-prefixed versions, such as ::-webkit-input-placeholder to patch support for ::placeholder in older Webkit browsers.

I’ve often felt it excessive—like using a hammer to crack a nut. And I’ve wondered if it might be doing more harm than good, by leading me to believe I have a magical sticking plaster for non-supporting browsers when actually (especially in the case of IE) the specific way in which a browser lacks support might be more nuanced. Furthermore I’ve never liked the noise generated by all those extra rules in my CSS output, especially when using the inspector to debug what would otherwise be just a few lines of CSS.

But I always felt it was a necessary evil.

However, I’ve just removed gulp-autoprefixer from my boilerplate. Why? Because:

  1. Browsers are no longer shipping any new CSS with prefixes, and as at 2019, they haven’t been for years;
  2. With the browsers that do require prefixed CSS now old and in the minority, it feels like progressive enhancement rather than “kitchen sink” autoprefixing should take care of them. (Those browsers might not get the enhanced experience but what they’ll get will be fine.)

Jen Simmons’ tweet on this topic was the push I needed.

So I’ve removed one layer of complexity from my set-up, and so far nothing has exploded. Let’s see how it goes.

Cookie Consent by Osano

The most popular drop-in solution to the EU Cookie Law requirements.

Over the last year I’ve been successfully using Cookie Consent by Osano on a number of commercial websites. Essentially this is a banner which appears at the bottom (or top) of your website and asks the visitor to explicitly give (or decline to give) consent for the cookies your website uses. It’s a great free resource which handles the requisite GDPR requirements (and more) and offers a number of customisation options.

it’s very simple to include and use – you just step through their WYSIWYG generator, include the generated JavaScript-based settings in your site, and point to their CSS and JavaScript libraries. I like self-hosting my own static assets so I integrate the libraries into my code rather than linking to their externally hosted files, but that’s just my personal preference.

Why do we need this?

It’s because In 2018, the European Union’s General Data Protection Regulation (GDPR) went into effect, establishing a number of principles governing the collection of personal information. Any company or individual that processes personal information of European Union citizens must comply with the GDPR, regardless of where data is stored or processed.

Cookies often collect information about their users that is not specifically identified with one individual, but if that information, combined with other data, can be used to identify an individual, it becomes “personal information” for the purposes of the GDPR and must be treated as such.

The clearest and most effective way to notify a user in advance of the collection of information using cookies is to provide a web banner or “pop-up” cookie notice that appears automatically when the home page is accessed for the first time, and requires some affirmative action.

Real Favicon Generator

Knowing how best to serve, size and format favicons and other icons for the many different device types and operating systems can be a minefield. My current best practice approach is to create a 260px × 260px (or larger) source icon then upload it to Real Favicon Generator.

This is the tool recommended by CSS-Tricks and it takes care of most of the pain by not only generating all the formats and sizes you need but also providing some code to put in your <head> and manifest.webmanifest file.

Everybody in the place

Enjoyed this acid house history lesson from Jeremy Deller – especially some of the footage from the early Manchester scene which I hadn’t seen before.

Nightclub graphic from the documentary “Everybody in the place”
Nightclub shot from “Everybody in the place”