jekyll html webdev aboutblog liquid minimal-mistakes

Better Post Metadata with Minimal Mistakes and Jekyll

I’ve put a lot of thought into the template that renders my page footer, filling it with conditionals and date filters and other Liquid craziness, and I think you’ll enjoy the results on your own blog.

Modified and Published

With stock Minimal Mistakes, you can add last_modified_at to your post front matter with a value of the following format:

yyyy-mm-ddThh:mm:ss<+/->hh:mm

For example, an update date of 6:30:50pm Pacific Time on July 16th, 2019 (which is UTC minus 7 hours because we’re observing Daylight Savings Time) would look like this: last_modified_at: 2019-07-16T18:30:50-07:00.

Now that renders like this:

Updated: July 16, 2019

Here are the contents of the post template (_layouts/single.html) that display that footer:

{% if page.last_modified_at %}
  <p class="page__date"><strong><i class="fas fa-fw fa-calendar-alt" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].date_label | default: "Updated:" }}</strong> <time datetime="{{ page.last_modified_at | date: "%Y-%m-%d" }}">{{ page.last_modified_at | date: "%B %d, %Y" }}</time></p>
{% elsif page.date %}
  <p class="page__date"><strong><i class="fas fa-fw fa-calendar-alt" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].date_label | default: "Updated:" }}</strong> <time datetime="{{ page.date | date_to_xmlschema }}">{{ page.date | date: "%B %d, %Y" }}</time></p>
{% endif %}

Digging into the conditional Liquid tags, one can see that the “Updated:” text (or its equivalent in the site’s locale) is concatenated with the original publish date, and if last_modified_at has been defined, the original publish date is replaced with “Updated: [last_modified_at]”.

The way this works doesn’t reveal the effort you (hopefully) put in to maintaining both a published and modified date.

The Fix

To fix this issue we need to add an entry to the post footer that displays the original publish date to complement the most recent modification date.

Minimal Mistakes is built for localization from the start, so let’s do that here as well. Considering I only know English, I’ll use that here.

In _data/ui-text.yml, under the en array (or whatever language your site uses), add the original_date_label key and set it to “Published:”, as seen in this diff:

# English (default)
# -----------------
en: &DEFAULT_EN
...
  date_label                 : "Updated:"
+ original_date_label        : "Published:"

To access this new string from the Liquid tag we can use site.data.ui-text[site.locale].original_date_label (which will be referred to as the original date label), and to display the modified dates you’ll need to concatenate them with date_label (which comes with the theme but is not to be confused with the “original date label”).

The simple fix is to replace the if-block in the post layout with the following:

{% if page.last_modified_at %}
  {% if site.data.ui-text[site.locale].original_date_label != null %}
    <p class="page__date"><strong><i class="fa fa-fw fa-calendar" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].original_date_label }}</strong> <time datetime="{{ page.date | date_to_xmlschema }}">{{ page.date | date: "%B %-d, %Y" }}</time></p>
  {% endif %}
  <p class="page__date"><strong><i class="fa fa-fw fa-calendar" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].date_label | default: "Updated:" }}</strong> <time datetime="{{ page.last_modified_at | date_to_xmlschema }}">{{ page.last_modified_at | date: "%B %-d, %Y" }}</time></p>
{% elsif page.date %}
    <p class="page__date"><strong><i class="fa fa-fw fa-calendar" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].date_label | default: "Updated:" }}</strong> <time datetime="{{ page.date | date_to_xmlschema }}">{{ page.date | date: "%B %-d, %Y" }}</time></p>
{% endif %}

Let’s break down the changed logic:

  1. If the post has a modified date and the original date label is defined for the site’s locale, display the original date label, concatenated with the original post date (post.date).
  2. If the post has a modified date, display the “updated” date label, concatenated with the last_modified_at date.
  3. If the post does not have a modified date and has an original post date, display the “updated” date label, concatenated with the original post date.

This doesn’t alter any of the theme’s behavior, but simply adds an item to the post footer when possible.

Note the replacement of the “%d” placeholder with “%-d”. This makes reading single-digit days much easier, as “07” becomes “7”. Here’s a snippet of all the supported date placeholders that are used:

Placeholder Output Example
%B Full month name (3-9 characters) January
%d Zero-padded day of the month (2 characters) 01
%-d Day of the month (1-2 characters) 1
%Y Year and century (4 characters) 1970

Additionally, I’ve chosen to simply use the date_to_xmlschema formatter for the HTML time element, for consistency and SEO purposes. It’s definitely valid; see this MDN page:

Valid datetime Values

a valid global date and time string

2011-11-18T14:54:39.929-04:00

Rebuild your site and voila:

Updated and Published dates in the post footer

Times!

Now that you put all that work into always displaying publish dates, why not expose the times?

Timezone

First your site should know about your timezone so that the times you add to the post front matter aren’t converted to UTC, which they are by default.

Go to _config.yml and set timezone to your region’s tz database code from this list on Wikipedia, like "America/Los_Angeles" for Pacific Time. This is a Jekyll-specific configuration you may have already completed, and you very well may be building your site on your own computer, in which case the OS and therefore Jekyll timezones are what you expect them to be.

In the following code blocks I’ll be using “local time” to refer to your site’s time zone, so feel free to replace that with whatever your time zone actually is.

Conditions

We shouldn’t always display the date and time of page.date and page.last_modified_at because if you haven’t defined the time, those components of the dates will be filtered to 12am sharp in your time zone. It’s unlikely you’ll actually post something at that second, so let’s check for it.

{% assign twelveHourOriginalDate = page.date | date: "%r" %}

The use of the %r date filter sets twelveHourOriginalDate to a string of the format hh:mm:ss MI, where “MI” is the meridian indicator (AM/PM).

Now we need to make sure it isn’t equal to “12:00:00 AM”, like so:

{% if twelveHourOriginalDate != "12:00:00 AM" %}
	{% assign originalDisplayDate = page.date | date: "%B %-d, %Y at %r local time" %}
{% else %}
	{% assign originalDisplayDate = page.date | date: "%B %-d, %Y" %}
{% endif %}

A similar thing can be done for the modified date:

{% assign twelveHourModifiedDate = page.last_modified_at | date: "%r" %}
{% if twelveHourModifiedDate != "12:00:00 AM" %}
	{% assign modifiedDisplayDate = page.last_modified_at | date: "%B %-d, %Y at %r local time" %}
{% else %}
	{% assign modifiedDisplayDate = page.last_modified_at | date: "%B %-d, %Y" %}
{% endif %}

Now they can be displayed in the footer by rendering the contents of originalDisplayDate or modifiedDisplayDate.

Render it!

Putting it all together, the contents of the post footer’s if-block in the single page layout should now look like the following:

{% assign twelveHourOriginalDate = page.date | date: "%r" %}
{% if twelveHourOriginalDate != "12:00:00 AM" %}
  {% assign originalDisplayDate = page.date | date: "%B %-d, %Y at %r local time" %}
{% else %}
  {% assign originalDisplayDate = page.date | date: "%B %-d, %Y" %}
{% endif %}
        
{% if page.last_modified_at %}
  {% if site.data.ui-text[site.locale].original_date_label != null %}
    <p class="page__date"><strong><i class="fa fa-fw fa-calendar" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].original_date_label }}</strong> <time datetime="{{ page.date | date_to_xmlschema }}">{{ originalDisplayDate }}</time></p>
  {% endif %}
          
  {% assign twelveHourModifiedDate = page.last_modified_at | date: "%r" %}
  {% if twelveHourModifiedDate != "12:00:00 AM" %}
    {% assign modifiedDisplayDate = page.last_modified_at | date: "%B %-d, %Y at %r local time" %}
  {% else %}
    {% assign modifiedDisplayDate = page.last_modified_at | date: "%B %-d, %Y" %}
  {% endif %}
          
  <p class="page__date"><strong><i class="fa fa-fw fa-calendar" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].date_label | default: "Last Updated:" }}</strong> <time datetime="{{ page.last_modified_at | date_to_xmlschema }}">{{ modifiedDisplayDate }}</time></p>
{% elsif page.date %}
  <p class="page__date"><strong><i class="fa fa-fw fa-calendar" aria-hidden="true"></i> {{ site.data.ui-text[site.locale].date_label | default: "Published:" }}</strong> <time datetime="{{ page.date | date_to_xmlschema }}">{{ originalDisplayDate }}</time></p>
{% endif %}

That’s it!

Now published and modified dates will have times when provided by you, the writer!

The final result with both updated and published times displayed in the author's time zone

That’s all I have time for today. Stay tuned!

Sources

Further Reading