Today I added a calendar to my website, and I was inspired by these two blog posts: Activity Calendar Partial and Generate Yearly and Monthly Archive Pages with Hugo Sections.

Table of Contents

Partials file structure

Here are the partial files I created in the layouts/partials/ folder and they were organized in the following structure.

|-- layouts
|  |-- partials
|  |  |-- archy.html
|  |  |-- archm.html
|  |  |-- archd.html
|  |  |-- calendar.html
|  |  |-- calendar
|  |  |  |-- year.html
|  |  |  |-- month.html
|  |  |  |-- day.html
|  |  |-- post
|  |  |  |-- archive.html

calendar.html, year.html, month.html, day.html files were downloaded from the Activity Calendar blog post. However, I edited the calendar.html and day.html files to allow users to click on a date in the calendar and go to the article directly (if there was only one article posted on that day).

In calendar.html under {{ range ($pages.GroupByPublishDate "2006-01-02") }}, add:

{{ range ($pages.GroupByPublishDate "2006-01-02") }}
    {{ $context.Scratch.SetInMap "ArticlesPerDay" .Key (len .Pages) }}
    {{ range .Pages }}
        {{ $context.Scratch.Set (.PublishDate.Format "2006-01-02") .Permalink }}
    {{ end }}
{{ end }}

Then edit day.html, add these lines:

{{ $link := $context.Scratch.Get $dateString  }}

<li class="calendar-day {{ if $isFuture }}calendar-day-future{{ end }} {{ if gt $articlesFound 0 }}calendar-day-has-articles{{ end }} {{ if eq (now.Format "2006-01-02") $dateString }}calendar-day-is-today{{ end }}">
    {{- if eq $articlesFound 1 -}} <!-- exactly one article that day -->
        <a href="{{ $link }}" title="{{ index (last 1 (split (delimit (split $link "/") "," "") ",")) 0 }}">
        <time datetime="{{ $dateString }}">{{ $day }}<em>{{ $articlesFound }}</em></time></a>
    {{- else if gt $articlesFound 1 -}} <!-- multiple articles on one day -->
        <a href="/archive/{{ delimit (slice $year $monthTwoLetters $dayTwoLetters) "-" }}/" title="{{ $articlesFound }} article{{ if gt $articlesFound 1 }}s{{ end }}">
        <time datetime="{{ $dateString }}">{{ $day }}<em>{{ $articlesFound }}</em></time></a>
    {{- else -}} <!-- no articles that day -->
        <time datetime="{{ $dateString }}">{{ $day }}<em>{{ $articlesFound }}</em></time>
    {{- end -}}
</li>

The code for three types of listing (by year, month or day) were partly adapted from templates provided in Generate Yearly and Monthly Archive Pages with Hugo Sections.

archy.html archy.html
1
2
3
4
5
6
7
{{ $year := .Date.Format "2006" }}
<h2>&emsp;Archive for {{ $year }}</h2>
{{ range where .Site.Pages "Type" "post" }}
    {{ if eq (.Date.Format "2006") $year }}
        {{ partial "post/archive" . }}
    {{ end }}
{{ end }}
archm.html archm.html
1
2
3
4
5
6
7
8
{{ $year := .Date.Format "2006" }}
{{ $month := .Date.Format "January" }}
<h2>&emsp;Archive for {{ $month }} {{ $year }}</h2>
{{ range where .Site.Pages "Type" "post" }}
	{{ if and (eq (.Date.Format "2006") $year) (eq (.Date.Format "January") $month) }}
		{{ partial "post/archive" . }}
	{{ end }}
{{ end }}
archd.html archd.html
1
2
3
4
5
6
7
8
9
{{ $year := .Date.Format "2006" }}
{{ $month := .Date.Format "January" }}
{{ $day := .Date.Format "02" }}
<h2>&emsp;Archive for {{ $month }} {{ $day }} {{ $year }}</h2>
{{ range where .Site.Pages "Type" "post" }}
  	{{ if and (eq (.Date.Format "2006") $year) (eq (.Date.Format "January") $month) (eq (.Date.Format "02") $day)}}
    	{{ partial "post/archive" . }}
  	{{ end }}
{{ end }}
/layouts/partials/post/archive.html /layouts/partials/post/archive.html
1
2
3
4
5
6
7
8
9
10
11
12
{{ $year := .Date.Format "2006" }}
{{ $month := .Date.Format "January" }}
<section class="boxes">
	<div class="archive archive-year box" data-date="{{ $year }}">
		<ul class="archive-posts archive-month" data-date="{{ $year }}{{ $month }}">
			<li class="archive-post archive-day">
     			<a href="{{ .Permalink }}">{{ .Title }}</a>
     			<span class="post-date">{{ .Date.Format "02 Jan 2006" }}</span>
			</li>
		</ul>
	</div>
</section>

Taxonomy structure

The idea is to archive blog posts by date and users can click on the date on the calendar to go to a specific post. In order for this to work, I added a new way of grouping the posts for this website (aka “Taxonomy”).

In the configuration file config.toml, add:

# adding taxonomy term 'archive'
[taxonomies]
    archive = "archive"

Then I created a layout file archive.html to layouts/taxonomy/ folder.
Here I used a Hugo function findRE, which returns a list of strings that match the given regular expression.

/layouts/taxonomy/archive.html /layouts/taxonomy/archive.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{{ partial "head.html" . }}
  <body>
    <div id="blog">
      {{ partial "header.html" . }}
      {{ partial "sidebar.html" . }}
      <div id="main" data-behavior="{{ .Scratch.Get "sidebarBehavior" }}"
        class="{{ with .Params.coverImage }}hasCover{{ end }}
               {{ if eq .Params.coverMeta "out" }}hasCoverMetaOut{{ else }}hasCoverMetaIn{{ end }}
               {{ with .Params.coverCaption }}hasCoverCaption{{ end }}">

        {{ $year := .Date.Format "2006" }}
        {{ $currentTaxonomy := index (last 1 (split (delimit (split .URL "/") "," "") ",")) 0 }}
        {{ $regex := index (findRE "-" $currentTaxonomy 2) 1 }}
    
        {{ if eq $currentTaxonomy $year }} <!-- archive by year -->
            {{ partial "archy.html" . }}
        {{ else if eq $regex "-" }} <!-- archive by day -->
            {{ partial "archd.html" . }}
        {{ else }} <!-- archive by month -->
            {{ partial "archm.html" . }}
        {{ end }}
	
      </div>
    </div>
  </body>
{{ partial "foot.html" . }}

Specific date format and the archive variable is required in the front matter of markdown files:

# examplepost.md
---
date: 2020-05-07T17:15:22.000Z
archive: ["2020","2020-05","2020-05-07"]
---

Render calendar style

I compiled the styling.scss file to css format at sassmeister.com, then added the css file to the statics/css folder. The calendar style (font size, color, etc.) can be altered by editing the css file /static/css/style.css.
For hugo to reconize customized css files, toggle the customCSS option in config.toml:

 # Custom CSS. Put here your custom CSS files. They are loaded after the theme CSS;
 # they have to be referred from static root. Example
   [[params.customCSS]]
     href = "css/style.css"

If your website is already using a custom css, simply copy and paste the code for calendar styling into the custom css file.

Add the calendar to the frontpage

Call the calendar partial in the layouts/index.html file, and specify start month and end month:

{{ partial "calendar" (dict "context" . "from" 2020 "fromMonth" 1 "to" (now.Format "2006") "toMonth" (now.Format "1") "pages" .Site.Pages) }}

Now the calendar should be visible on the website! Pretty awesome. Check out a live demo.

Issues and solution

Issue 1: The background circles for ‘calendar days with articles’ and ‘today’ are not showing.

Hugo websites with themes such as Tranquilpeak and XMag may have this issue when rendering the calendar.

Solution:

In the /static/css/style.css file, add opacity: 0.9 under .calendar-day.calendar-day-has-articles and .calendar-day.calendar-day-is-today.

.calendar-day.calendar-day-has-articles {
  color: #ffffff;
  position: relative;
  opacity: 0.9;
}
.calendar-day.calendar-day-is-today {
  position: relative;
  font-weight: 800;
  color: #ff6600;
  opacity: 0.9;
}

Also move .calendar-day.calendar-day-future { opacity: 0.4; } to the bottom of the file so that the opacity for future dates renders the last.

Issue 2: Links to categories and tags are not working after adding the calendar to the website.

Hugo websites with theme XMag may have this problem.

Solution:

Make sure that the desired taxonomy terms are all included in the config.toml file.

[taxonomies]
  archive = "archive"
  tag = "tags"
  category = "categories"

Issue 3: Days with articles are not correctly displayed in the calendar.

Hugo websites with theme XMag may have this problem.

Solution:

Try changing .GroupByPublishDate to .GroupByDate in the layouts/partials/calendar.html file.

{{ range ($pages.GroupByPublishDate "2006")   }}
{{ range ($pages.GroupByPublishDate "2006-01")  }}
{{ range ($pages.GroupByPublishDate "2006-01-02") }}
# Change to .GroupByDate