I have been using Hugo-PaperMod as a theme for quite some time. It is pretty lightweight and yet flexible. For the past 4-5 years, I have been working with this Hugo based blog and I have definitely learned a lot about the structure of a Hugo website and made several modifications to the original theme but just on my blog and shared about it earlier.

There was one thing that I hadn’t done for quite sometime. Primarily because I really don’t have the time to keep modifying themes. I barely have time to write about things I want to write. But today I bit the bullet and decided that I was going to do it. This is partly because my wife was reviewing the latest Resume page that I added and she told me that the navigation had to be scrolled horizontally! And as someone who has built user friendly platforms most of my professional career, this hurt. So off to work I went.

Like any back-end dev who does a bit of front-end dev, I started with searching the web. I stumbled upon PaperMod-Responsive which I got to through a reddit post. The repo also had a demo.

All thanks to the awesome work of Arpreet!

How did I integrate just the menu?

The menu of the hugo-papermod theme is deeply embedded into the original header.html. This makes it difficult to override the menu on its own without making a copy of the entire file.

If you are wondering why copy the entire file, that’s how Hugo works. If a theme has a partial and you want to customise it, you create a partial with the same name in your Hugo website.

.(site root)
|--layouts/
   |--partials/
      |--header.html
|--theme/
   |--papermod/
      |--layouts/
         |--partials/
            |--header.html

When you do this, hugo will use your header.html.

Got my header, now what?

Go add the following button markup before the unordered list that displays the menu. That is line 4 of the HTML snippet below.

1
2
3
4
5
6
7
...
{{- $currentPage := . }}

<button id="menu-trigger" aria-haspopup="menu" aria-label="Menu Button"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentcolor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg></button>

<ul id="menu">
...

Now change the unordered list that you spotted there on line 6. Remove the id attribute of the ul element below the button you added and instead add a class.

1
<ul class="menu hidden">

Alright, now you have setup the markup ready for the custom css and javascript.

What CSS?

If you want to add custom CSS to your website that uses Hugo papermod, it goes under the following directory

.(site root)
|--assets/
   |--css/
      |--extended/
      |--your-css-file-name.css

In my case, that file name is custom.css. Not very creative with the naming there. Alright, time to add the css for your menu.

 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
.menu {
    display:flex;
    margin:auto var(--gap);
    list-style:none;
    word-break:keep-all;
    overflow-x:auto;
    white-space:nowrap
}

.menu li+li {
    margin-inline-start:var(--gap)
}

.menu a {
    font-size:16px
}

.menu .active {
    font-weight:500;
    border-bottom:2px solid
}

@media screen and (max-width:768px) {
    .menu {
     list-style:none;
     position:absolute;
     right:0;
     z-index:5;
     margin-bottom:var(--gap);
     padding:var(--gap);
     background:var(--code-bg);
     border-radius:var(--radius);
     border:2px solid var(--tertiary);
     line-height:2.5;
     margin:calc(var(--header-height) - var(--gap))var(--gap);
     display:block
    }
    .list .menu {
     background:var(--entry)
    }
    .menu li+li {
     margin-inline-start:0
    }
    #menu-trigger {
     font-size:30px;
     position:relative;
     display:block;
     float:right;
     margin:auto var(--gap)
    }
    .hidden {
        display:none
    }
}

@media screen and (min-width:769px) {
    #menu-trigger {
     display:none
    }
}

Next up javascript

In a hugo website, if you need to include custom js, you follow a similar tactic. But in papermod, the author recommends, you add custom js and refer to it in a special layout file.

Let’s first create the javascript file.

.(site root)
|--assets/
   |--js/
      |--extended/
      |--your-js-file-name.js

In my case, I called it menu-trigger.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let menuTrigger=document.querySelector("#menu-trigger");
let menu=document.querySelector(".menu");

menuTrigger.addEventListener("click",function(){ 
    menu.classList.toggle("hidden")
});

document.body.addEventListener('click', function(a) {
    menuTrigger.contains(a.target) || menu.classList.add("hidden");
});

Now you have to ensure that this javascript file is included in your site. when it is deployed.

In papermod, you do this by following the instructions on the FAQ on custom head and footer.

Then in your extend_footer.html add the following:

1
2
{{ $navMenu := resources.Get "js/extended/menu-trigger.js" | js.Build "menu-trigger.js" | minify | fingerprint }}
<script language="javascript" type="text/javascript" src="{{ $navMenu.Permalink }}"></script>

That’s all you need really. You should now have a button menu when you look at your website on a screen that had a width less than 768px.