Introduction

I built my old site (not this one) with Hugo and PaperMod and hosted on Netlify.

My old site is still accessible via https://main–cheerful-mousse-b9d87b.netlify.app, and here’s the GitHub repo. For the rest of this post, I’ll refer to it as my (old) PaperMod site.

In this post, I provide an overview of Hugo and PaperMod and describe the modifications I made to the original theme in setting up my old site. Specifically, my PaperMod_diff page shows diffs between my site and the original theme in HTML pages, and you should refer to it. Instead of bloating this post with inline code, I’ll let you know the relevant files, and you can check out the diffs (well-documented, with citations of where I found code from).

I have now switched to Jekyll using the al-folio theme (though the theme matters a lot more than the static site generator, imo). I provide comparisons of PaperMod and al-folio throughout this post.

Why did I switch?

Obviously, something must’ve gone wrong for me to switch. Before I switched, I had kept track of all the changes I made to PaperMod in this post, and I also updated the post a bit. If you’d like to make a PaperMod website and aren’t satisfied with its base functionality, then this post would be pretty helpful for you and save you a lot of time.

On the flip side, this post also serves as a review of PaperMod’s functionality (rather, all the missing functionality). It’s possible to implement these features, as I describe here, but the implementations I found are suboptimal and just not worth the effort, in my opinion.

In short, there are a ton of missing features in PaperMod. Check the table of contents on the sidebar under the My website heading for the important missing features I noticed and implemented or found implementations for. It turns out that implementations of all of these missing features already exist and are submitted in PRs and issues, but the creator of PaperMod rejects the code. I suppose there’s value in keeping PaperMod simple and minimal, but I spent several weeks implementing and searching for implementations of these important features. And several implementations I found are suboptimal.

On the other hand, al-folio already implements all of these features well. Switching over to al-folio took a day of work and just involved messing with the config file and copying over the Markdown content, with some minor tweaks. I hardly tweaked the theme itself at all, and I have found only one minor bug so far.

Before proceeding, I want to preface the rest of this post by saying that the creators and contributors of PaperMod and al-folio are all way more skilled than me and have contributed a ton to the community. I think al-folio is better overall, but both themes are great, and making them obviously took a lot more work and skill than writing a little blog post. I’m just sharing my experience and opinions.

Audience

This post was written with two audiences in mind: people who are new to Hugo and people with Hugo experience. If you’re new to Hugo, then I recommend reading this post from start to finish. If you have experience with Hugo, then I recommend skimming. Specifically, I would read Clone/fork for setup instructions. Then skip over the Hugo/PaperMod overview to get to My website, which describes specific features of my website. Please see the Table of Contents for an overview.

Credit

This post is inspired by Konstantin’s similarly-titled blog post.

Resources

Before proceeding, I’d like to share five excellent resources that I used and reference throughout this post. The top three are crucial because they cover Hugo and PaperMod. The bottom two describe very specific features that I reference in My website, so don’t worry about those yet.

My goal is to make the rest of this post self-contained (and link to brief external resources). If something doesn’t make sense, please use the first three resources.

As mentioned earlier, I also created PaperMod diff 6 to show modifications I made to the theme in a readable way. In short, to modify a file from the theme, you create a copy of the file and make edits in the copy, but this makes it hard to see what was modified. So, PaperMod diff shows diffs between the files.

Setup

First, follow the steps here to install Hugo. You should also have Git installed. 7

Clone/fork

Run these commands.

git clone --recurse-submodules --no-single-branch https://github.com/jesse-wei/jessewei.dev-PaperMod.git
cd jessewei.dev-PaperMod
hugo server

--recurse-submodule clones the PaperMod submodule.

Additionally, you may clone with --depth=1 to save some disk space.

hugo server starts up a server for you to view the site.

Why clone/fork?

At this point, you could start from scratch instead of cloning/forking my website. However, resources 1 and 2 already describe how to start from scratch.

So, I’ll cut to the chase and have you clone my website and describe changes I made.

If something doesn’t make sense, then I recommend first reading/watching the resources.

In addition, if you notice some specific feature of my website that I don’t explain, then use inspect element to inspect the code for that feature. Then run grep -ir in this repo to find the relevant code. -r makes the search recursive, and -i makes the search case-insensitive. You can also use VSCode’s code-searching feature with Ctrl/Cmd+Shift+F. Lastly, you can also leave a comment below.

Overview of Hugo and PaperMod

Skim this section or skip to My website if you’re already familiar with Hugo and PaperMod (e.g., if you read/watched the resources).

Repo structure

Please read Hugo’s directory structure (3 min) and the top part of Hugo’s content organization (1 min) for a general overview of Hugo’s directory structure. I’ll describe more in-depth below.

Here’s the structure of my repository. I omit unimportant stuff and stuff I don’t use, and certainly changes will be made, but this is all the important stuff:

jessewei.dev-PaperMod
├── assets                  Overrides PaperMod/assets. Contains mostly CSS, some JS
│   └── css
├── config.yml              Site-wide configuration file
├── content
│   ├── about.md
│   ├── archives.md
│   ├── classes
│   ├── discord.md
│   ├── posts               List layout
│   ├── privacy.md
│   ├── projects            List layout
│   ├── search.md
│   └── teaching            List layout
│       └── act             List layout within list layout
├── layouts                 Overrides PaperMod/layouts
│   ├── _default            Layout of entire pages (specifically, the <main> element)
│   │   └── single.html
│   └── partials            Layout of components of a page
│       ├── comments.html
│       ├── extend_head.html
│       ├── footer.html
│       ├── header.html
│       ├── index_profile.html
│       └── social_icons.html
├── scripts                 My scripts
├── static                  Images, etc.
│   ├── SAPsim_still_cropped.jpg
│   ├── SAPsim_still_full.jpg
│   └── ...
└── themes
    └── PaperMod

There are 4 crucial parts of the repo: config, content, layouts and assets, and static.

config.yml

This is the configuration file for the website containing all site-wide parameters.

content/

This is the directory where site content (posts) goes.

Site content should be Markdown files.

The front matter of a Markdown file contains metadata about the post. For example, the front matter of this post is

---
title: "Overview of Hugo/PaperMod and Setting Up This Site"
date: 2023-05-14T20:13:59-04:00
draft: false
cover:
    image: img/hugo_logo_wide.svg
    alt: "Hugo logo"
    caption: "Hugo logo"
    hidden: false
summary: "This post provides an overview of Hugo (PaperMod theme) and details the steps I took in setting up this website."
tags: ["Hugo", "PaperMod", "Markdown", "HTML", "CSS", "Blog", "Website", "Portfolio"]
---

This information is used to generate the post’s page. It’s quite intuitive what these fields do (check by seeing how something in front matter renders on the page), so I won’t go into detail.

For a list of variables you can use, see [Variables Front Matter](https://adityatelange.github.io/hugo-PaperMod/posts/papermod/papermod-variables/#page-variables) from PaperMod documentation.

layouts/ and assets/

The files in these directories override the files in themes/PaperMod/layouts/ and themes/PaperMod/assets/, respectively. If the path of a file in layouts/ exactly matches that of a file in themes/PaperMod/layouts/, then your site will use the file in layouts/ instead of the one in themes/PaperMod/layouts/. Same for assets/.

Essentially, themes/PaperMod/layouts/ and themes/PaperMod/assets/ specify defaults. If you want to make a change, override the default in your own repo.

layouts/ contains HTML files that specify the structure of pages.

Let’s look at PaperMod/layouts/_default/.

themes/PaperMod/layouts/_default
├── _markup
├── archives.html
├── baseof.html
├── index.json
├── list.html
├── rss.xml
├── search.html
├── single.html
└── terms.html

baseof

In particular, here’s baseof.html. Note that double braces { { } } (without space between braces) are used in the actual code, but I will be using single braces from now on so the code can be correctly rendered in this page.

<!DOCTYPE html>
<html lang="{ site.Language }" dir="{ .Language.LanguageDirection | default "auto" }">

<head>
    {- partial "head.html" . }
</head>

<body class="
{- if (or (ne .Kind `page` ) (eq .Layout `archives`) (eq .Layout `search`)) -}
{- print "list" -}
{- end -}
{- if eq site.Params.defaultTheme `dark` -}
{- print " dark" }
{- end -}
" id="top">
    {- partialCached "header.html" . .Page -}
    <main class="main">
        {- block "main" . }{ end }
    </main>
    { partialCached "footer.html" . .Layout .Kind (.Param "hideFooter") (.Param "ShowCodeCopyButtons") -}
</body>

This is the base template for all pages. Notice it has all parts of an HTML document: <!DOCTYPE html>, <html>, <head>, and <body>.

It’s mostly HTML. However, note the code in braces { { ... } } or { {- ... -} }. This is Go template code. It’s a templating language that Hugo uses to generate HTML.

Note on line 2 that a site variable site.Language is directly inserted into an HTML attribute. Some site variables are in config.yml, and others are built-in to Hugo.

Note on line 5 that a partial head.html is inserted. A partial is an HTML snippet that can be inserted into a page. As we can see here, the partial head.html (which can be found under layouts/partials/head.html) is the <head> code of all pages.

Does this mean all pages have the same <head> code?

Nope, notice one last thing on line 9: conditionals! By checking the values of some variables, we can conditionally insert HTML code. Lines 9-14 just insert some classes, but it’s also possible to insert entire HTML snippets or partials. So although all pages have the same template for <head>, the actual <head> code depends on parameters.

On lines 16 and 20, we see partials defining the header and footer of a page. In the middle is <main> for the content of a page. All files in _default except baseof.html define <main>.

single

Most pages are singles (layouts/_default/single.html). For example, this post on my original site is a single.

A portion of single.html is below.

{- define "main" }

<article class="post-single">
  <header class="post-header">
    { partial "breadcrumbs.html" . }
    <h1 class="post-title">
      { .Title }
      {- if .Draft }<sup><span class="entry-isdraft">&nbsp;&nbsp;[draft]</span></sup>{- end }
    </h1>
    {- if .Description }
    <div class="post-description">
      { .Description }
    </div>
    {- end }
    {- if not (.Param "hideMeta") }
    <div class="post-meta">
      {- partial "post_meta.html" . -}
      {- partial "translation_list.html" . -}
      {- partial "edit_post.html" . -}
      {- partial "post_canonical.html" . -}
<!-- Rest of code omitted -->

Notice this code goes in the "main" block from line 17 of baseof.html. You could inspect element a single page on my old PaperMod site to confirm. The code is quite intuitive, so you should be able to see how this code (in addition to front matter and site variables) causes certain elements to appear at the top of the page.

Specifying layout of a page

You should rarely have to manually specify the layout of a page in front matter. Hugo determines whether a page is a single or list by directory structure. Most pages should be singles, of course.

You can see an example of a list layout at my projects page. However, I did not specify this layout manually: It’s automatically a list layout because projects/ has directories but no index.md file.

content/projects
├── 566
├── leds
├── mips_emulator
├── neuroruler
├── rubiks_541
└── sapsim

I have two pages where I manually set the layout in front matter. One is Search, and the other is Archives.

Here’s the front matter of search.md, and it’s similar for archives.md.

---
title: "Search"
layout: "search"
summary: "search"
---

This topic is further described in Content.

static/

The fourth and final important part is static/. This is where static files, such as images, go.

I don’t have very many images here because I prefer to group images with the post itself in content/.

Note that after compilation, files in static/ are copied to the root directory /. So, when accessing a file in static/, you should prepend a / to the file path. For example, the image static/1.jpg should be accessed as /1.jpg. In practice, I’ve found that the leading / can often be omitted. Just know that something like /static/1.jpg won’t work.

Site logos are configured in two places in the config.

params:
  # OpenGraph image displayed when posting site link on socials
  # For example, if you post the link to the site in Discord, this image will be displayed
  # This image is used with <meta property="og:image" ...> and <meta property="twitter:image" ...>
  images: ["logo_outlined_6.png"]

  # ...

  # Logo and name shown on top left of site
  label:
    text: "Jesse Wei"
    icon: /logo_filled_outlined_6.png
    iconHeight: 35

It’s pretty self-explanatory. I do want to show an example of how the website looks when posted in Discord.

OpenGraph image Discord
OpenGraph image in Discord

Favicons

See the PaperMod documentation. favicon.io is very convenient for generating favicons!

Shortcodes

I want to mention shortcodes. There are times when you need raw HTML (e.g., figure with centered caption, YouTube video embed, etc.), and shortcodes are shortcodes for doing so. Quite a few are built in to Hugo and PaperMod, and they’re very convenient.

Shortcodes are not built-in to al-folio, to my knowledge, however. In al-folio, the convention is just to inline the raw HTML, so that’s what I’ll be doing to replicate the result of the shortcodes (see my old PaperMod site for the actual results). If you inspect the source code, then you’ll see how convenient shortcodes are!

Raw HTML

{< rawhtml >}
<p align="center"><strong>This is raw HTML</strong></p>
{< /rawhtml >}

This is raw HTML

Figure

{ < figure src="img/social_logo.jpg" caption="OpenGraph image Discord" alt="OpenGraph image Discord" align="center">}
OpenGraph image Discord
OpenGraph image in Discord

Hugo documentation

YouTube embed

{ < youtube hjD9jTi_DQ4 >}

The YouTube embed looks a lot better in PaperMod than it does in al-folio.

Hugo documentation

GitHub gist

{ < gist jesse-wei 0b2472f020b41b8767882291c536102c >}

Hugo documentation

Deploy

Resource 2 describes how to deploy a Hugo site to Netlify. Here’s a timestamp for that portion of the video.

The build process is incredibly simple. In the video, the only command you input for the build process is hugo.

My build process involves slightly more than just hugo since I also have to build PaperMod_diff 6. So, I use scripts/netlify. My build command in Site settings > Build & deploy > Build command is chmod +x scripts/netlify;./scripts/netlify.

You can ignore that unless you also want to set up a PaperMod_diff page.

al-folio comparison

al-folio deployment is easier because it already includes a GH action for deploying to GH pages. You just need to name your repo {your GH username}.github.io and follow some other steps in the README.

However, deploying the site locally on an M1 MacBook was a huge pain in the ass. For some reason, deploying via Docker wasn’t working, which is super weird. Then Ruby and Jekyll installation was not straightforward. I eventually got local deployment to work after following this YouTube video.

My website

Now I’ll describe specific features of my website.

config.yml

The latest version of my config.yml is here.

I think I use reasonable values, and I use comments to explain decisions I consider non-obvious. I’ll explain some specific decisions I made in this file in the below sections as they come up.

al-folio comparison

I was very confused by the meanings of several parameters in PaperMod’s config.yml and had to read PaperMod’s documentation about several parameters. Plus, there are some super weird things that I had to modify. For example, my /teaching page had the page title Teachings even though the URL was "teaching", which was super confusing. After Googling, it turns out I had to set pluralizeListTitles to false, but this parameter doesn’t even exist in PaperMod’s base config.yml? In addition, this is obviously a feature that should be off by default, so I’m not sure why it’s enabled by default.

al-folio’s config.yml is commented super well, and I had no issues.

PaperMod diff

I created PaperMod diff 6 using the scripts in scripts/. diff.py runs diff between corresponding files, helpers/generate_directory_index_caddystyle.py creates index.html files recursively, and build wraps diff.py to deploy its output to Netlify (see Deploy). helpers/cd.py is also used.

Do note that content/posts/papermod_diff is gitignored. This is because if it weren’t, then the content there would change and be shown on GitHub every time I modify assets/ and/or layouts/, which is redundant and would make the commit history harder to read. My solution (gitignore the folder and generate it during the build process in Netlify) is a bit roundabout, but it’s already implemented and works well.

Content

This section is a continuation of Specifying layout of a page.

Let’s look more closely at the structure of content/teaching, which is a list layout.

content/teaching
├── act               List layout within list layout
│   ├── _index.md     Note, _index.md, not index.md!
│   ├── binary
│   ├── desmos
│   ├── eulers_formula
│   ├── ...
├── comp110
│   ├── img
│   └── index.md
├── comp210
│   ├── img
│   └── index.md
└── comp311
    ├── img
    ├── index.md
    └── review

My Teaching page has a list layout because teaching/ doesn’t have an index.md. The comp110/ directory is a single because it has an index.md. It’s accessible by /teaching/comp110. It also contains an img/ directory that’s accessible from index.md. The images could go in /static/, but I prefer bundling them with the page.

It’s possible to have a list layout within a list layout, and /teaching/act is an example. However, notice act/ must have an _index.md file (note the underscore) since it’s a non-leaf.

See Page Bundles for more details.

al-folio comparison

Honestly, this was (and still is) a bit confusing to me. I can see how it is more organized, but I don’t see why it’s necessary.

al-folio’s directory structure feels just like that of a raw HTML and CSS website and was much easier to get used to.

\(\LaTeX{}\)

I enabled \(\LaTeX{}\) via KaTeX in layouts/partials/extend_head.html.

I followed Math Typesetting from PaperMod documentation. Specifically, the code in extend_head.html is mostly from Issue #236.

I modified the condition for loading the KaTeX script. The site param math must be true. Then KaTeX will be loaded by default in all pages. Setting the local param math to false in front matter will cause that page to not load KaTeX.

I think having to opt-in is super annoying, so I’d rather enable it globally and be able to opt-out.

al-folio comparison

al-folio uses MathJax by default. As shown here, MathJax is on par or faster than KaTeX. In my experience, MathJax is also less restrictive.

Comments

I enabled comments using giscus.

I followed the directions on the giscus site to install giscus in my repo and pasted code from the giscus website into layouts/partials/comments.html. I also added comments: true to config.

As you can see, there are comments at the bottom of almost every page. PaperMod automatically disables comments in the index profile, search, and archives layouts. I manually disabled comments in my Privacy policy page in the front matter with comments: false.

Comments show up in GitHub Discussions. Make sure to enable Discussions in your GitHub repo.

However, the comment box does not automatically change color on theme toggle if all you do is add the script. I found an implementation online for it, and it’s in layouts/partials/comments.html.

al-folio comparison

al-folio implements giscus (and Disqus, deprecated) by default. The only change needed is some configuration in config.yml. In addition, the comment box automatically toggles color when the theme is toggled.

I added social icons to the footer, as in resource 4. I sort of follow what it describes but make some of my own adjustments.

As described there, adding social icons to footer messes with CSS spacing values. For example, a scrollbar appeared on the homepage and Search page (haven’t solved this and don’t plan to) even though there’s enough room for both header and footer to be visible without scrolling. This issue is described more in-depth in resource 4, under problem 2.

In short, I modified CSS in 4 files. layouts/partials/footer.html, layouts/partials/social_icons.html, assets/css/core/theme-vars.css, and assets/css/common/profile-mode.css. The comments in each file describe the changes I made. Most comments in social_icons.html are for htmltest, described below, so ignore those for now.

I disabled footer social icons on the homepage because the homepage already has social icons.

al-folio comparison

I didn’t bother with this since al-folio puts social icons in the footer of the homepage by default. That’s fine with me.

Beyond that, I made some other minor changes to the footer.

I added the separator character • between phrases in the footer.

The links were originally like this:

Powered by
<a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
<a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>

I removed target=_blank and rel="noopener noreferrer" because links should not usually open new tabs.

I added a privacy policy page and a link to it in the footer.

Lastly, I removed “Powered by Hugo and PaperMod” in the homepage specifically to keep it minimal.

al-folio comparison

It’s much easier to change the text of the al-folio footer because you need only edit config.yml.

However, it seems that all external links open in new tabs in al-folio. Personally, I don’t like that, but I get that the purpose is to get the user to stay on your site. It’s fine with me since the behavior is consistent, but I might end up changing this.

Single

I slightly modified layouts/_default/single.html. I moved the ToC above the cover. Notice on this page that the ToC is above the cover image.

This was just basic CSS.

I modified CSS in the following CSS files in assets/css/common/: archive, footer, header, main, post-entry, and post-single.

For example, main.css has these important lines:

/* Change color on hover */
a:hover {
    color: var(--carolina_blue);
}

a.anchor:hover {
    color: var(--carolina_blue) !important;
}

svg:hover {
    color: var(--carolina_blue);
}

For modifications, look for comments and the variable carolina_blue. Check PaperMod diff.

Since I made links blue on hover, I removed underline on hover (the link is still underlined if it had an underline before hovering though).

al-folio comparison

al-folio colors links and underlines on hover by default. All I did was change the theme colors to UNC blue in _sass/_variables.scss and _sass/_themes.scss.

Syntax highlighting via Chroma

I disabled highlight.js (default) and enabled Hugo Chroma following the steps in PaperMod documentation. This required a few changes in config.yml and assets/css/extended/*.css.

I disabled line numbers by default in config.yml for readability. Most code blocks you’ve seen so far have not had line numbers.

However, you can enable line numbers for a specific code block, as shown in the baseof code block, by adding {lineNos=true} to the code block. 8

It was very easy to install just one Chroma theme. However, I also wanted the code theme to toggle when the theme toggles. This should’ve been decently easy because all I had to do was a second stylesheet with a dark code theme and add .dark to the class names. However, I ran into a lot of issues with the light theme CSS overriding the dark theme CSS for most combinations of themes. It could’ve been a fault in my implementation, but without changing anything, the specific combination of pygments (light) and dracula (dark) didn’t have any issues, so I’m not sure what was going on.

Plus, there’s a second issue. Line numbers on code blocks look weird.

Line numbers on code blocks look weird
Line numbers rectangle has a rounded edge by DOCTYPE and /body

al-folio comparison

Everything works well by default! I changed $code-bg-color-light in sass/_variables.scss, but I think that’s it.

However, Hugo’s code syntax highlighter supports go-html-template (I just specify html or text in this post) and arduino, whereas Jekyll’s doesn’t. In addition, in PaperMod, I didn’t have to convert double braces to single braces inside code blocks like I had to do in this post.

Table of Contents

This one is quite important to me but is horrible to implement on PaperMod.

On this post, notice the ToC on the side (if your screen is wide enough) that automatically changes color to show you where you are in the post. In addition, since there’s not enough room to show all the headings, it only shows all the top-level headings and expands the lower-level headings of the heading you’re currently on. I think this is a very good feature for organization too since you don’t get much out of seeing lower-level headings if you’re not reading that section.

There’s a fork of PaperMod called PaperModX that roughly implements this. However, it is not a good implementation, and that’s the best implementation I found (compared to two others in a PaperMod PR and a PaperMod issue). That implementation will show all headings and will display a scrollbar (such as on this page in my PaperMod website) if there are too many headings. It does highlight the active heading, but if there’s a scrollbar, then it won’t scroll down when you get to the bottom, and the active element feature will then break completely. GH issue with more information.

If you’re somehow still interested in implementing this feature in PaperMod, then the relevant code is in assets/css/common/post-single.css (for putting the ToC on the side and hiding it on small screens), layouts/partials/footer.html (for a short script - I’m not sure what it does), and layouts/partials/toc.html (adds a few classes to the toc div and the script for scrolling and keeping track of the active ToC element). Check PaperMod diff 6.

al-folio comparison

The Bootstrap ToC plugin just works! See the documentation.

The only thing I would change is that on small screens, the side ToC (if enabled) disappears, and there’s nothing to replace it. In the implementation described above, if there’s not a side ToC, then the ToC is at the top of the post (as in normal PaperMod).

I did check what happens with the Bootstrap ToC when there are enough headings to fill the screen by resizing my window. There is no scrollbar, and the ToC just overflows the screen and is cut off. However, realistically speaking, this should never occur, and even if it does, the top-level headings will still be shown.

Hamburger/responsive menu

There should be a hamburger menu for small screens. Several people asked for an implementation of this, but Aditya refuses to merge the change. What’s funny is he implemented it himself and sent the relevant code in Discord but chooses not to merge it into the repo…?

Here’s the commit where I copied in the code, and here’s the relevant issue.

al-folio comparison

You’ll never guess…

al-folio supports this by default. In addition, al-folio also supports dropdown menus and allows you to choose between sticky and fixed header and footer.

Miscellaneous bugs and shortcomings

PaperMod

The scrollbar is bugged on Safari. More information and a fix. This is a problem with the original theme.

Link underlines either disappear or turn the wrong color on theme toggle. More information, and I have not found a solution for this. This is a problem on PaperMod’s site too.

This isn’t really a bug, but in PaperMod, if you use a footnote more than once, then the footnote at the bottom of the post will just specify ↩ ↩ ↩ …, one arrow for each footnote reference. However, al-folio specifies ↩ ↩2 ↩3 …

al-folio

Have found only one bug! So far… 🤞

On the sidebar ToC, at least with a sticky navbar, if you click a heading, then the page will scroll such that the heading is at the top of the page, and the navbar above that. However, the active ToC element probably won’t be the heading you clicked on because the sticky navbar above takes up some space, so the script thinks the active element is the heading above the one you clicked on.

This isn’t a bug, but I would like it if the side ToC ignores the automatically-generated heading “Enjoy Reading This Article?”

Google Analytics

For Google Analytics, just add your Google Analytics tag to googleAnalytics in the config. For example, mine has googleAnalytics: G-Q603T56FWT.

PaperMod automatically uses the Google Analytics script if env is production (default). See the bottom of layouts/partials/head.html:

{- /* Misc */}
{- if hugo.IsProduction | or (eq site.Params.env "production") }
{- template "_internal/google_analytics.html" . }
{- template "partials/templates/opengraph.html" . }
{- template "partials/templates/twitter_cards.html" . }
{- template "partials/templates/schema_json.html" . }
{- end -}

al-folio comparison

al-folio also has Google Analytics by default.

CI

I added a GH workflow for checking links in my site and spellcheck. This is not directly related to PaperMod and al-folio.

htmltest GH Action output
htmltest GH Action output

See .github/workflows/ci.yml and its configuration file .github/.htmltest.yml. This follows resource 5, with some modifications. In particular, I want to note that I run my scripts/build in ci.yml instead of just hugo, as in the original file.

Behavior

Here is the intended behavior of the htmltest job after making the modifications below.

If an internal link (e.g., a page or image) doesn’t work, the workflow will fail, causing a red X to appear on GH Actions.

If an external link doesn’t work, htmltest will warn, but the workflow will not fail. That is, an external link could be “broken,” and a green checkmark will be shown on GH Actions. This is fine because when using a lot of external links, it’s unlikely that all will work in any single run. There could be a timeout or non-200 HTML response code, etc.. Getting a red X when just a single external link breaks is annoying. htmltest does still warn, so I manually check GH actions every now and then.

Getting rid of garbage output

There was originally >100 lines of garbage output. htmltest complained that the site logo’s link at the top left had no alt text, and my LinkedIn link in social icons in the footer returned non-OK exit status 999. Since the header and footer are in all pages, this caused a lot of errors, which made the output unreadable.

I fixed this in layouts/partials/header.html by adding non-empty alt text to the logo and in layouts/partials/social_icons.html by excluding the LinkedIn link from htmltest using the data-proofer-ignore attribute, as specified in htmltest’s README.

The top 3 links are ones I want to keep even though they don’t actually work. So I manually ignored them in the post (not layouts/) using the data-proofer-ignore attribute in rawhtml. 9

And the #center thing is how you can center an image in Markdown syntax in PaperMod. #center also gets appended to an image URL if you use align="center" in figure shortcode. But since this causes htmltest to freak out, I added this to .htmltest.yml:

IgnoreURLs:
  # Ignore <img src="*#center"> for centered images in PaperMod, which would cause "hash not found"
  # This is suboptimal because we ideally want to check the image URL without the #center suffix

  # Match internal image path ending in #center
  - .*\.(apng|gif|ico|cur|jpg|jpeg|jfif|pjpeg|pjp|png|svg)#center$$
  # Match external image URL ending in #center
  - (https://|http://|www\.).*\.[A-Za-z]+#center$$

As you can tell by the comments, this is suboptimal and can lead to false negatives. I can think of two solutions.

  1. Modify htmltest to ignore specific hashes but check the rest of the URL 10
  2. Modify PaperMod to center the image without appending #center

Make GH Actions display red X on failure

Instead of continue-on-error: true from resource 5, I use if: always().

- name: Test HTML
  # https://github.com/wjdp/htmltest-action/
  uses: wjdp/htmltest-action@master
  with:
    config: ./.github/.htmltest.yml
- name: Archive htmltest results
  # Archive result even if Test HTML fails
  # Use if: always() instead of continue-on-error, as in the original file
  # Source: https://stackoverflow.com/questions/62045967/github-actions-is-there-a-way-to-continue-on-error-while-still-getting-correct
  if: always()

if: always() will cause logging to occur even if Test HTML fails. continue-on-error: true does the same. However, continue-on-error: true would cause GH Actions to display a green checkmark when Test HTML fails, which is misleading.

I added this to .htmltest.yml:

# This does not "ignore" the broken links, but it does not fail the action
# From the htmltest README:
# When true produces a warning, rather than an error, for broken external links.
IgnoreExternalBrokenLinks: true

With a lot of external links, it’s unlikely that all external links will work during any one run. Maybe there’ll be a timeout or bad HTML response code, etc.

Conclusion

There are definitely some other modifications I made to PaperMod that I didn’t describe here, but these are the major points.

I think this post pretty much confirms 100% that, at the moment, Jekyll + al-folio is better than Hugo + PaperMod. Looking into the future, however, Hugo is growing a lot faster than Jekyll is (and imho, it overall does feel better than Jekyll). I can’t be sure if I made an optimal long-term choice, but al-folio is really dang good, and I don’t think I’ll have to completely refactor my website again any time soon.

If you’re using PaperMod, then I hope this post makes it easy for you add some of the necessary features. However, whether you’re using PaperMod or deciding whether to use it or anything else, I hope you consider al-folio!

  1. Hugo Quick Start  2

  2. Getting Started With Hugo  2

  3. PaperMod demo site/documentation and its source 

  4. Konstantin’s How to Set Up This Blog  2 3

  5. Check links in Hugo with htmltest  2 3

  6. PaperMod_diff  2 3 4

  7. I assume you already do, surely. 

  8. You might also be able to enable it by default in a specific post by adding it to front matter, but this didn’t work for me. 

  9. TODO: Create shortcode for a link with data-proofer-ignore attribute. 

  10. This would ignore any URL with #center suffix, not just image URLs.