Hugo templates - wonderful templates and how to modify them

Welcome to CSS jungle, we’ve got tags and divs / We got everything you want honey, we know the links
We are the people that can style whatever you may need / If you agree the CSS rules, we got your properties — Welcome to CSS Jungle1

What you just read, it’s been taken from a famous Guns n’ Roses song. It is nothing more than a frontend developer parody of the famous song “Welcome to the jungle”.

Well, it perfectly describes how I felt when I was dealing with CSS: imagine being in the Tarzan’s Jungle, but instead of lianas you have tags.

Note that this article has been written after modifying the template: this means that the process may have been slightly changed to simplify code.

Context

As you may (or may not) know, I’ve changed the design of my website several times: this is because I’ve been undecided in the past, and I tried to figure out what was the best layout and the best features for a website.

The reason behind this is really simple: I never found a perfect template that was complaining about my needs. And this time is not an exception: the template I’ve been using was missing two basic things:

  • a nice font
  • social / contact icons: those icons you see on some websites which let people contact you

The template I used is really beautiful, but this time, it was different: I was really up to change the template to comply with my needs. This means that, if I had found a way to implement some icons in the left sidebar with a href, the template would have been suitable for me.

Let’s get this done!


The plan

Indeed, there are some steps that I thought were ok to follow:

  1. figure out where is the left sidebar
  2. figure out how it is defined
  3. find a way to take parameters from config.YAML – we will talk about it later
  4. fetch an image about a social brand, possibly an icon (without blaming too many people and wasting too much time)
  5. find a way to link YAML parameters and the image with a href
    1. conditional image: if YAML parameters are not defined, the image must not be rendered
  6. style and load CSS

Where is the left sidebar?

The first thing I wanted to know: where the left sidebar was defined. “Oh, it’s easy” – I thought – “just open the page, look out for the page that is being loaded and you’ve done”.

He-he, it was too easy.

So I discovered the first thing: Hugo uses special templates called “partial templates”. It allows to render things using variables, and these templates are meant to be used in several parts of your website.

└── layouts
    └── partials
        ├── footer
        │   ├── scripts.html
        │   └── site-footer.html
        ├── head
        │   ├── favicons.html
        │   ├── metadata.html
        │   ├── prerender.html
        │   └── twitter.html
        └── header
            ├── site-header.html
            └── site-nav.html

From Hugo Docs2

Since what are we looking for is a sidebar, we are probably looking for sidebar.html.

Luckily, in our template folder, there is a left.html file, that is exactly what are we looking for. (it is under layouts/partials/sidebar/)

Ready for step 2?


How is it defined?

Let’s take a look at this snippet:

<header class="site-info">
	{{ with .Site.Params.sidebar.avatar }}
		{{ if (default true .enabled) }}
		<figure class="site-avatar">
			{{ if not .local }}
				<img src="{{ .src }}" width="300" height="300" class="site-logo" loading="lazy" alt="Avatar">
			{{ else }}
				{{ $avatar := resources.Get (.src) }}

				{{ if $avatar }}
					{{ $avatarResized := $avatar.Resize "300x" }}
					<img src="{{ $avatarResized.RelPermalink }}" width="{{ $avatarResized.Width }}"
						height="{{ $avatarResized.Height }}" class="site-logo" loading="lazy" alt="Avatar">
				{{ else }}
					{{ errorf "Failed loading avatar from %q" . }}
				{{ end }}
			{{ end }}

			{{ with $.Site.Params.sidebar.emoji }}
				<span class="emoji">{{ . }}</span>
			{{ end }}
		</figure>
		{{ end }}
	{{ end }}
	<h1 class="site-name"><a href="{{ .Site.BaseURL }}">{{ .Site.Title }}</a></h1>
	<h2 class="site-description">{{ .Site.Params.sidebar.subtitle }}</h2>

We know that HTML is a markup language3: then, any element in a page must be defined by HTML (or JS, which luckily it’s not our case).

This looks like a normal HTML page, except for these small details:

  • graph parenthesis {{ $avatarResized.Height }}
  • if-else structure {{ if $avatar }} and {{ end }}
  • .Site.BaseURL that looks like something Java class-related

I don’t know how Hugo manages variables, but that .Site.BaseUrl made me think about the possibility of being a variable.

So it is time to phase 3!


Fetching params from config.YAML

In this phase, I will just briefly explains how Hugo’s YAML works in general.

We usually have a .toml (or YAML) file in our root directory, that will set several variables that are used by our partial templates (obviously I didn’t know they were used by these last ones; I would have saved up so much time) to render certain elements, such navbars and social icons.

Let’s have a look into the example YAML file:

sidebar:
	emoji: 🍥
	subtitle: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
	avatar:
		enabled: true
		local: true
		src: img/avatar.png

Looking at the previous snippet and comparing it to our YAML, we find out something’s interesting: .Site.Params.sidebar.subtitle and sidebar part in YAML.

So, the subtitle is such a child of the sidebar, and we refer to it in a partial template by calling {{.Site.Params.father.son}}

We need to talk about the conditional:

{{ if $avatar }}

Interestingly, I end up with something like “if defined then…”, which sounds amazingly comfortable to me, because I could show up things in a custom way.

If you understand this logic, we can now implement our icons.


Fetch images

Ok, now we need to fetch our images – why? – you may ask.

We have found a way to connect HTML and the YAML file, so we need to render at least one picture.

To find a way how to download several icons without blaming anyone, I chose to go to FontAwesome website and choose several icons to download.

If you’re under Linux, you can use a very comfy script (knowing the name of the icon you want to download):

#!/bin/sh
set -ex

#########
## VAR ##
#########
target_folder=fontawesome
icons="twitter github facebook-square"	# add your fontawesome icon name here
url=https://raw.githubusercontent.com/FortAwesome/Font-Awesome/master/svgs/brands

mkdir -p "${target_folder}"
for icon in $icons; do
  icon="${icon}.svg"
  wget -O "${dest}/${icon}" "${url}/${icon}"
done

In this particular example, we will download in fontawesome folder, facebook, twitter and github icons.

Obviously, you could download whether SVG you want, you are not limited to svgs/brands folder.

Let’s now move our folder with our icons into theme/hugo-stack-theme folder.

We need to create an additional file in layout/ directory named fontawesome.html with this content:

{{- $fname:=print "themes/hugo-stack-theme/fontawesome/" . ".svg" -}}
{{- $path:="<path" -}}
{{- $fill:="<path fill=\"currentColor\"" -}}
{{ replace (readFile $fname) $path $fill | safeHTML }}

We have now created a partial layout containing an icon. But… we still haven’t finished!

First of all, note that we can access our “element” by writing this shortcode:

{{ partial "fontawesome.html" "github" }}

Knowing this will access to a big decision: we need to define into YAML file a section, then link it into partial html and define a link when you click an image.

Into YAML, I went for:

socialmenu:
	enable: true
	linkedin: "" # your linkedin username
	instagram: "" # your instagram username
	github: "" # your github name
	twitter: "" # your twitter username
	email: "" # your email
	facebook: "" # your facebook username

For partial: do you remember the file at the beginning? layout/partials/sidebar/left.html?

Well, we need to modify it and include our “variable reading”; to do this, we just need to understand where we want to put our social icons.

I like to put this kind of thing under the subtitle: so I located where “reading the subtitle” was implemented and written under it something like that:

{{ if (.Site.Params.socialmenu.enable) }}
	{{ if (.Site.Params.socialmenu.email) }}

		<a href="mailto:{{.Site.Params.socialmenu.email}}"><span class="inline-svg" >{{ partial "fontawesome.html" "envelope" }}</span></a>

	{{ end }}
{{ end }}

What does this snippet do? It loads the part after the first row if socialmenu variable is enabled.

Conditional CSS

The same goes for socialmenu.email: if it is defined it will “draw” a link and an image (do you remember our shortcode? Well, it’s time to use it).


Let’s style it!

Ready to get some style?

We have created a new file called socialicons.scss; inside it, we will put every single piece of code related to social icons on the left.

Let’s style our icons:

.inline-svg {
  display: inline-block;
  height: 1.15rem;
  width: 1.15rem;
  top: 0.15rem;
  position: relative;
}

This will define a new class called .inline-svg, which is the class we used for our FontAwesome images.

We still have one problem: social icons are displayed one on top of the other, not “in a row”. Solution?

Let’s use a flexible space to build the social sidebar menu. We would like to show four icons in a row, and the other icons need to be under the initial four ones.

We first create the flexible space, by creating a special div that will contain other divs (which are going to contain our icons).

In the left.html we add this just after the subtitle:

{{ if (.Site.Params.socialmenu.enable) }}
	<div class="social-icons">
		{{ if (.Site.Params.socialmenu.email) }}
			<div><a href="mailto:{{.Site.Params.socialmenu.email}}"><span class="inline-svg" >{{ partial "fontawesome.html" "envelope" }}</span></a></div>
		{{ end }}

		{{ if (.Site.Params.socialmenu.github) }}
			<div><a href="https://github.com/{{.Site.Params.socialmenu.github}}"><span class="inline-svg" >{{ partial "fontawesome.html" "github" }}</span></a></div>
		{{ end }}
		
		{{ if (.Site.Params.socialmenu.instagram) }}
			<div><a href="https://instagram.com/{{.Site.Params.socialmenu.instagram}}"><span class="inline-svg" >{{ partial "fontawesome.html" "instagram" }}</span></a></div>
		{{ end }}
		
		{{ if (.Site.Params.socialmenu.facebook) }}
			<div><a href="https://facebook.com/{{.Site.Params.socialmenu.facebook}}"><span class="inline-svg" >{{ partial "fontawesome.html" "facebook-square" }}</span></a></div>
		{{ end }}

		{{ if (.Site.Params.socialmenu.linkedin) }}
			<div><a href="https://facebook.com/{{.Site.Params.socialmenu.linkedin}}"><span class="inline-svg" >{{ partial "fontawesome.html" "linkedin" }}</span></a></div>
		{{ end }}
		
		{{ if (.Site.Params.socialmenu.twitter) }}
			<div><a href="https://twitter.com/{{.Site.Params.socialmenu.twitter}}"><span class="inline-svg" >{{ partial "fontawesome.html" "twitter" }}</span></a></div>
		{{ end }}
	</div>
{{ end }}    

Then, let’s style our main div and all the divs inside it:

.social-icons {
    display: flex;
    flex-wrap: wrap;
    flex-direction: row
}

.social-icons>* {
	flex: 0 0 25%;
	padding-left: 10px;
}

Note that 25%: we need to specify the amount that each space will take. In our case, we want four columns, so 100/4 = 25. That’s why 25! ;)

Everything seems to be perfect but, let’s try to resize our page: boom, everything is broken. We need to fix our elements on mobile!

So, let’s modify social-icons in a way that, when loaded on a mobile phone, it will render all the icons in a row:

@media screen {
    .social-icons>* {
        flex: 0 0 25%;
        padding-left: 10px;
    }
}

@media (max-width:960px) {
    .social-icons>* {
        flex: 0 0 5%;
        padding-left: 10px;
    }
}

Last step: let’s add our socialicons.scss to the main file: style.css by importing it with @import "socialicons.scss";.


The End

We’ve done, I hope you enjoyed the trip, just like me.

A huge thanks goes to Nick Galbreath4, without that article I never would have had the willpower to learn more about partials templates and Hugo.

You could find the result on this website or Github, in my local fork (unless it became merged).


Note: this article has been published on Medium too!