A Hugo shortcode and provided partial plus CSS capable of rendering a multi-column (up to 5 columns) timeline display of events with NO Javascript.
The project consists of three key files:
shortcodes/hugo-timeline.html
- The shortcode that ultimately generates a timeline display, one per page. This shortcode builds the timeline itself, without any events.partials/hugo-timeline-li-event.html
- A partial called by thehugo-timeline.html
shortcode. This partial is called once per event and is responsible for placement of the event display on the timeline built byhugo-timeline.html
.static/css/hugo-timeline.css
- The timeline-specific CSS used control layout of the timeline and events. Included inhugo-timeline.html
.
Apart from this README.md
file, a sample-data.md
timeline "page" is included as an example. The timeline and events displayed upon it are generated from the front matter provided in pages like sample-data.md
which makes a call to the hugo-timeline.html
shortcode.
Coming soon! hugo-timeline
is NOT a theme, and since there are so few files involved it can be installed as a Hugo module. Details are provided below.
I maintain an example timeline rendered in my personal blog at https://blog.summittdweller.com/timeline.
- The timeline is rendered from a Hugo shortcode, so it is essentially "called" from within any page by placing a
{{% hugp-timeline "." %}}
reference in the page's Markdown (.md
) file. - The can be only one timeline per page, but you can have an unlimited number of timelines in any given site.
- The timeline "grid", and events displayed in the timeline, are governed by front matter metadata in the aforementioned
.md
file. - Each timeline is rendered in reverse chronological order, with the most recent event at the top of the graph.
- Each timeline can display between 1 to 5 columns of events. The number of columns is also determined in the page's
.md
file, so there is no fixed number of colums per site. - The timeline must have a specifed
startYear
in the.md
front matter, and it may have an optionalendYear
. If noendYear
front matter is provided the timeline will be "current", ending with December of the current year. - Timeline granularity is per month, and it always displayed by full January-to-December years. For example: front matter values of
startYear: 2017
andendYear:2021
would create a timeline that spans from January 2017, to December 2021. If theendYear
parameter is omitted the timeline would span from January 2017 until the end of the current year, at the time of this writing that's December 2022. - Events may be discrete (a single
to
date with nofrom
) or they may have duration with both afrom
andto
start and end date/time. - In an event, if a
from
date is specified with noto
date, the event is assumed to be "current" and its top will always be the current month. - If an event
link
is provided, clicking on thename
/title of the event will open the linked site/page in a new tab using thetarget=_blank
attribute. - Clicking on any portion of an event other than the
name
/title, if alink
is provided, will expand the event listing so that the entiredescription
is visible.
The idea for a Hugo timeline project began with a meeting of the Rootstalk development team in September 2022. The team, and our 2022 Vivero fellow in-particular, had an interest in developing a simple timeline to chronicle events related to Rootstalk and the contriutors who create it.
The Rootstalk team initially considered implementing a relatively simple timeline using the Timeline.js package, perhaps displayed in an iframe
on the Rootstalk home page. However, the notion of introducing so much Javascript in the Rootstalk structure without damaging other capabilities made this option relatively unattractive.
We then turned to the MetalBlueberry Hugo Timeline Shortcode project, one that uses very little Javascript. Unfortunately, the timeline's structure and restrictions didn't really fit what we wanted for Rootstalk since there's not support for multiple columns, overlapping events, or discrete/single-day events. This code also doesn't appear to "scale" events, it just places and labels them, rendered in the sequence in which they are listed, as a "stack" of event "blocks."
While looking for a timeline that might be suitable for Rootstalk, we also came across this splendid Jekyll timeline project: https://github.com/SimplGy/jekyll-timeline. The demo pages provided for it are spectacular! Unfortunately, Rootstalk's site, and virtually all other sites were I'd like to have a timeline, are generated in Hugo.
I won't go into all the reasons I choose Hugo over Jekyll here, but suffice it to say I have enough interest in Jekyll to keep a close eye on projects built for that platform. I also have reason to try and learn as much about Jekyll as I can since I really like the CollectionBuilder project and the Lib-Static approach to digital preservation.
So began my first foray into Jekyll-to-Hugo conversion. The product of that conversion is this new repo and this README.md
file.
As part of my Jekyll-to-Hugo conversion I kept notes regarding steps that I thought would be common in any similar template transformations. The notes first appeared in the comments of this repo's hugo-timeline.html
shortcode. They include:
Common Jekyll to Hugo changes...
- Replace all opening '{% comment %}' tags with Hugo comment opening: curly-curly-slash-asterisk
- Replace all closing '{% endcomment %}' tags with Hugo comment closing: asterisk-slash-curly-curly
- Regex replace all '{% assign (.+) = ' tags with '{{ $$$1 := ' and note the space after the = sign!
- Replace all closing ' %}' with ' }}'
- Replace all opening '{% ' with '{{ '
I made all of the above changes, and many more, using VSCode to edit the source.
I used the guidance posted in Hugo Modules: Getting Started to make a first distribution of this repo's components as a Hugo module.
Keep this module current in your projects by updating them using:
hugo mod get -u github.com/SummittDweller/hugo-timeline
...or to update ALL Hugo modules...
hugo mod get -u
I solved this issue without need for a z-index
. The key is this default CSS behavior: If two positioned elements overlap without a z-index specified, the element positioned last in the HTML code will be shown on top.
So in the hugo-timeline.html
shortcode I changed this line...
{{ range $event := $.Page.Params.col1.events }}
...to this...
{{ range $event := sort $.Page.Params.col1.events "to" "desc" }}
I did the same for all other columns. Now the events sort correctly such that their default placement has the desired effect with the oldest events floating to the "top" of the stack so their titles and descriptions remain visible.