Coding Babble  Code it once more, with feeling.

How to Create a Hugo Theme

By Luke Simpson

Introduction

This site is created with Hugo and also uses a custom theme that I built. I was using another theme, but I wasn’t a huge fan of how it was set up. For example, running the hugo command should trigger the production path, but in this case it didn’t because the theme was using a nonstandard variable in hugo.toml to control that. That’s not ideal since hugo.toml gets checked into version control. I also prefer using the CLI with the -e argument to control the environment rather than set an environment variable, but that had no effect with the theme.

It also seemed to make arbitrary decisions, like adding a robots meta with noindex,nofollow to each page if it wasn’t built for production, so my site wasn’t getting indexed by search engines, and I didn’t realize it.

Rather than go find another theme that would probably have its own quirks, I decided to make my own. At least this way, I will be aware of everything that’s going on, and I’ll be able to add onto it as needed.

My Goals

Add your theme

In your Hugo project:

hugo new theme name

Theme content

When you first make your theme, it will give you some default content in themes\name\content. If you already have content for your site, you won’t need this, however you may need to override some of it.

For example, I had to add the following content to my blog in order to override the ones that came with the theme:

hugo new content/_index.md
hugo new content/posts/_index.md

After that you can just set the post-1.md, post-2.md, and post-3/index.md in your theme to draft status to hide them.

Customize <head>

Support for Google Analytics

Luckily, Hugo already has an internal template for Google Analytics.

{{ template "_internal/google_analytics.html" . }}

However, it won’t render anything unless you have included your analytics ID in hugo.toml.

[services]
[services.googleAnalytics]
ID = 'G-XXXXXXXXXX'

We also only want to render it for production, so we can surround it with a condition.

{{ if eq hugo.Environment "production" }}
  {{ template "_internal/google_analytics.html" . }}
{{ end }}

Google recommends putting it immediately after the <head> element, so paste it at the top of themes\name\layouts\partials\head.html.

Now when we build our site with the hugo command, which sets the environment to production by default, it will add the analytics scripts.

If you don’t want the scripts, you can specify any other environment, e.g.:

hugo -e testing

Since hugo server serves a development environment by default, you won’t have to worry about it affecting your analytics.

Meta information

You’ll probably want to add some custom meta info to the <head>, such as a description and keywords. Google hasn’t cared about the keywords meta since 2009, but other search engines may still use it. Google still respects description, though. I added this to <head>:

{{ if .Description }}
  <meta name="description" content="{{ .Description }}" />
{{ end }}

{{ if .Keywords }}
  <meta name="keywords" content='{{ delimit .Keywords "," }}' />
{{ end }}

{{ delimit .Keywords "," }} takes an array and outputs a comma-separated string.

So if you populate the built-in description or keywords variables in the front matter of your posts, the meta info will be added to the head automatically.

Add author to posts

I also wanted to add my name to the posts, so I added the following code to themes\name\layouts\_default\single.html:

{{ if .Params.author }}
  <p id="author">By <span>{{ .Params.author }}</span></p>
{{ end }}

So if the author property exists in the front matter, the author will be displayed.

Custom partial template

At the end of my blog posts I have a message linking to my t-shirt store. This was done with a partial template so that I don’t have to link to it manually in the content of every article.

To do this, I added support.html to themes\name\layouts\partials\ with the following content:

<p>Support message goes here.</p>

However, if you scroll down, you’ll see a different message. That is because the above content is just the default for the theme itself and can be customized for your individual Hugo project by adding another support.html file to layouts\partials. If a partial with the same name exists in your project as in the theme, it will use the one from your project instead.

Favicon

The default theme ships with a favicon.ico, but rather than use that, let’s add support for PNG.

Add this to head.html:

<link rel="icon" type="image/png" href='{{ printf "%s%s" site.BaseURL "favicon.png" }}' />

Now all you have to do is drop a favicon.png file into the static folder. Keep in mind your baseURL in hugo.toml should be set. If this file doesn’t exist, a 404 will be returned.

If you want your icon to show up in Google search results, they require a multiple of 48, so at least 48x48px.

You can also specify different sizes and special rel values for Apple icons.

Styles

At this point it’s just about styling the site how you want in themes\name\assets\css\main.css. You can also add IDs where you need them. For example, the tags at the bottom are in a bullet-pointed list by default. This list is created in themes\name\layouts\partials\terms.html. In order to style them differently from other ul elements, I gave the first div an ID of taxonomy:

<div id="taxonomy">
  <div>{{ $label }}:</div>
  <ul>
    {{- range . }}
    <li><a href="{{ .RelPermalink }}">{{ .LinkTitle }}</a></li>
    {{- end }}
  </ul>
</div>

Conclusion

I also added RSS compatibility. But as you can see, making your own theme doesn’t have to be complicated.

Support the blog! Buy a t-shirt or a mug!

Tags: