Create Your Own Hugo Theme From Scratch—With Snails
Tags: hugo, webdev, beginners
Published: 2021-02-27 04:51:49

Okay, you’ve tried hugo. It’s nice, it’s fast; it’s what the cool kids use. You shuffle from theme to theme and found nothing you like for your beloved blog.

What do you do?

That’s where I was in a month ago, and I created my own hugo theme from scratch. In this post, I’ll outline everything I did.

## Prerequisite
Here’s what you need to know before proceeding.
- HTML and templating engine (e.g., Jinja, handlebars, liquid, etc)
- little bit of hugo
- terminal (`cd`, `rm`, etc)

Alright, let’s dive straight to it.

## Create a new hugo site for the “shell”
This is the one confusion that has wasted hours of my life.

The theme you create must live inside a hugo site. Think of a snail—the theme is the slimy part, and the thing for testing your theme is the shell part.

This is how it looks like in “trees.”

```
.
└── site (shell of snail)
    ├── blahblahblah
    ├── content
    └── themes
        └── your-theme (the slimy part)
```

### Time to create
In the directory of your choice, run `hugo new site your-site-name`

The directory will look like this

```
.
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── static
└── themes
```

Notice the `themes` folder? Yeah, those are the folders we’re going to mess with.

What you have to do now is to create a new theme in that folder. You can do that with `hugo new theme your-theme-name`, but for me, that’s too barebones. Here’s what I did to get up and running ASAP.

1. cd to `themes`
2. `git clone https://github.com/vinliao/hugo-starter.git`
3. `rm -rf hugo-starter/.git` to remove git (so you can point to your own git remote)
4. `mv hugo-starter your-new-theme-name` to rename
5. `cd ../..` and in config.toml, add `themes = "your-new-theme-name"`

Now it looks—more or less—like this.

```
.
├── archetypes
│   └── default.md
├── config.toml
├── content
├── data
├── layouts
├── resources
│   └── _gen
│       ├── assets
│       └── images
├── static
└── themes
    └── your-new-theme-name
        ├── archetypes
        │   └── default.md
        ├── exampleSite
        │   └── config.toml
        ├── layouts
        │   ├── 404.html
        │   ├── _default
        │   │   ├── list.html
        │   │   └── single.html
        │   ├── index.html
        │   └── partials
        │       ├── footer.html
        │       ├── header.html
        │       └── nav.html
        ├── README.md
        └── static
            └── css
                └── style.css
```

Now `cd` to the root of this site—read: the “shell” of the snail—and run `hugo serve -D`. (The -D arg is for showing drafts).

Boom. It’s working... but it’s empty. To fill the emptiness, you can use the `hugo new your-post-name.md` command. 

Try it out—run `hugo new about.md`, then `hugo serve -D`, and go to `localhost:1313/about`.

## Theme details
I won’t get into the nitty gritty, but I’ll just explain the very basics of hugo templating engine—ELI5 fashion.

If you go to your theme folder—the slimy part of the snail—here’s what you’ll see.

```
.
├── archetypes
│   └── default.md
├── exampleSite
│   └── config.toml
├── layouts
│   ├── 404.html
│   ├── _default
│   │   ├── list.html
│   │   └── single.html
│   ├── index.html
│   └── partials
│       ├── footer.html
│       ├── header.html
│       └── nav.html
├── README.md
└── static
    └── css
        └── style.css
```

The most important folder is the layouts folder. At this stage, you can pretty much ignore everything else. Let’s zoom in there, shall we?

```
.
├── 404.html
├── _default
│   ├── list.html
│   └── single.html
├── index.html
└── partials
    ├── footer.html
    ├── header.html
    └── nav.html
```

How does all these relate to each other?
- `404.html` is self-explanatory
- `nav.html`, `header.html`, and `footer.html` is also self-explanatory
- `header.html` contains `nav.html`
- `index.html` contains `header.html` and `footer.html`. This is the thing that gets “served” when user visits `yourwebsite.com`
- `list.html` contains `header.html`. This is the thing that gets served when user views `yourwebsite.com/posts`
- `single.html` serves individual post

(Another way to see this: `list.html` is like the list of things that gets shown to you when you first open Hacker News; `single.html` is for HN comments.)

Here’s something that caught me offguard about all these files: the `header.html` and `footer.html` are, in the end, combined into one file. If you look at `header.html`, you notice that there’s an opening tag for `<main>, and <body>`, but no closing tag. Ding, ding, ding—the closing tag is on `footer.html`.

This structure is not a straitjacket rule. The theme I created (https://github.com/vinliao/mita) doesn’t adhere to this structure. In my theme, I want the list of post to show when user visits `vinliao.com` instead of having to go to `vinliao.com/posts`. I ended up changing the `index.html` to show lists of post instead.

Now that you understand, you can do whatever you want with your theme.

## Git
If you want to share this theme, share the slimy part, not the whole snail.

This means you must `git init` in the `your-theme-name` folder.

## Conclusion
This is not an in-depth post in any way, but at least, you can now fiddle with your theme.

Happy hacking, people of the internet!