In this post, I aim to explain this website. Template by template… The main purpose here is to create a document to myself for future reference. Templates are complicated and not easy to read. That’s why I’ll probably forget what I did and why I coded this way.
Taxonomies
We are using the default taxonomy called tags
. Hugo provides 2 taxonomies by default: tags
and categories
. You don’t need to specify them anywhere. I’ve added the configuration below to config.yaml
file to use only tags
and disable categories
taxonomy.
[taxonomies]
tag = "tags"
Templates
Hugo templates are stored in /layouts
folder. There are 2 types of templates: list templates and single templates. List templates are for listing content while single templates are for displaying the content itself. For example, /tags, /categories, /posts
use “list templates”; while /post/foo.html, /about
pages use “single page templates”.
Home Page
Home page is a stand-alone page created impilicty by hugo. I’ve used layouts/home.html
template to separate home page from others. If we want to add content to home page we should use /content/_index.md
file.
baseof.html & Hugo Blocks
Every other template is using baseof.html
. I was using a similar layout in jekyll called default.html
Here is an example of Hugo blocks. This block indicates where “footer” block will be placed. What’s defined inside this block is the default for the block. Default lines will be used if it’s not overwritten by some other template.
{{ block "footer" . }}
<footer>{{ partial "footer.html" . }}</footer>
{{ end }}
In another template, let’s say, list.html
, we define “footer” block. So, all the content in the footer block be placed in that part of baseof.html
. If there is no “footer” block defined in child template (list.html) then hugo will use the original lines from the baseof.html
. Which is <footer>{{ partial "footer.html" . }}</footer>
in this example.
In this site, footer is not variable but content is. I have content block in baseof.html and it has no lines of code inside which means if it’s not defined then it’ll be empty.
{{ block "content" . }}{{ end }}
And here is the definition of content from list.html
{{ define "content" }}
{{ partial "generate_button.html" (dict "href" "/series" "name" "Remove Filters")}}
{{ partial "list_pages_episodes.html" . }}
{{ end }}
Listing Content
In my setup there are 3 ways of listing blog posts.
- Tags (group by tags)
- Series (group by sections)
- Archives (group by date).
Filter All Blog Posts & Ordinary Pages
To display all pages across sections, we need to gather all the pages in the site. To do this I’ve used the following code.
{{ $pages := where $.Site.RegularPages ".Type" "!=" "ordinary" }}
Site.Pages
will gather all the pages including “index pages”, “term pages” and home pages for each section. Site.RegularPages
will filter out those pages still it will include pages like about.md
. To gather only the blog posts, either we should add a meta-data in blog-posts or the pages we don’t want to display. Since it’s easier to tag the pages I don’t want to display, I added type: ordinary
line in their front-matter.
There is one more way to filter those pages, I call ‘ordinary’. We can check if file contains date
in it’s front-matter. This will work since I won’t enter a date for ordinary pages. Yet, it’s not obvious to understand. I find setting a meta-data in the front-matter more clean and intuitive instead of using magic variables.
{{ where $.Site.RegularPages ".Params.date" "!=" "" }}
Also, we may need to check a single page to see if the page fits our criteria. To do that, we’ll not use RegularPages
but (eq .Kind "page")
. RegularPages is a shortcut for us. It checks if .Kind
is equal to "page"
for all pages and returns an array of pages.
In order to control if a single page is a post or not I use the following mechanism:
{{ if and (eq .Kind "page") (ne .Type "ordinary") }}
Tags View
/layouts/tags/list.html
/layouts/tags/term.html
These files overwrites the default list html /layouts/_defaults/list.html
for tag section.
list.html
is for creating the /tags
page and term.html
is for /tags/foo
where foo is a tag. In Hugo terminology; each tag is called as a “term” in “tags” taxonomy.
Layouts above also using these partials:
- generate_button
- tag_cloud
- get_post_infocard
- pagination
- list_pages
Archives View
- Layout:
/layouts/_default/archives.html
- Page:
/content/archives.md
Unlike tags, “series” and “archives” pages are custom pages and these are not taxonomies. Just single - ordinary pages. So, by default these pages use /_default/single.html
However, I’ve added layout
information in the front-matter.
---
title: Archives
layout: archive
type: ordinary
---
Archives view, is simply displays all blog posts in the website ordered by date and grouped by year-month.
Series View
- Layout:
/layouts/_default/series.html
- Page:
/content/series.md
Similarly with the archives view, series view has layout meta-data in it’s front-matter.
---
title: Archives
layout: series
type: ordinary
---
Hugo calls, each sub-folder in the /content
directory as sections. In this site, all blog posts will be there and each blog-post series will have their sub-directory. For example, I’ll create three blog-post series called “operations-research”, “jekyll” and “hugo”. Each blog-post of a series, as hugo called them sections, will have meta-data called “episode”.
Basically, series view do: group posts by sections then order by episode. Also, check “section.html” which can be considered as a term if we take series (sections) as a taxonomy.
Other Templates
I’ve two more templates, called list.html
and section.html
.
list.html
/layouts/_default/list.html
In my design, this template is not used by any of the pages. Because, tags are overwritten by their own files and I’ve created a separate section.html
for section pages.
section.html
As I said before, section.html
can be considered as a term for series view. This template is used by section pages (home pages for sections) which are "/hugo"
, "/jekyll"
, "/operations-research"
or "/foo"
.
If I delete this template, section pages will start using /layouts/_default/list.html
.
What if I don’t want to use any template?
Well, you don’t have to. When we use templates, we define parts inside blocks. If you don’t use any block, then the template won’t be related with baseof.html
. You can create a separate <html>
file, 404 page is a good example.
Blog Posts vs Series vs Projects
In my site, series are blog posts with episode
parameter in their front-matter. I’ve seen many blogs using the name “tutorials” where I use the name “series” instead. I am not sure if they created different taxonomy but I didn’t. Also there are projects which is again just another section.
I considered to create different taxonomies called “series” and “projects” to separate those from others. Then I realised I had to add category
taxonomy too and not use sections. As far as I understand, most people alredy don’t use sections. Taxonomies can even make somethings easier, like templating and filtering since Hugo provides built-in functions. However, I found using sections is easier, it requires less templates and folders. I don’t need to make things complicated since all these 3 are basically the same thing. Also, I don’t want to make front-matter longer.
I’ve customized projects section’s list template and single template by creating a separate folder for them /layouts/projects
. So, Hugo will use these templates instead of /layouts/_default
for projects section. Template Lookup Order is important here.