Introduction

In this article, I will explain the steps for creating a blog like mine: static, automatically generated and hosted for free.

Framework

I have built this blog using Hugo and the hello-friend-ng theme, and host it on GitHub Pages.

Workflow

I use a private Git repository to version the development side of the blog (such that drafts will not be public), and use a public repository named xphyro.github.io to host the public web site. I do not use GitHub Actions or any other external automations; rather, I set up a client-side pre-push hook in the development repository that builds the web site and pushes the build to the repository acknowledged by GitHub Pages.

Setup

Essentials

The following are the steps to set up a near-exact copy of this blog as of the time of writing this article.

  • Complete the quick start to get a basic idea of Hugo.
  • Navigate to the root directory of your Hugo project and add the hello-friend-ng theme as a sub-module by executing the following.
git submodule add 'git@github.com:rhazdon/hugo-theme-hello-friend-ng.git' themes/hello-friend-ng
  • Overwrite your config.toml file with the example file by executing the following
cp -f themes/hello-friend-ng/exampleSite/config.toml config.toml

Then, configure your website by filling in or changing the details.

Below are some important changes (for replication purposes) in my configuration. I have omitted changes that are clearly explained in the default configuration, or changes that are obvious.

[permalinks]
  posts = "/articles/:year/:month/:day/:filename/"

[taxonomies]
  tag      = "tags"
  category = "categories"
  series   = "series"

[languages]
  [languages.en]
    copyright = '<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noopener">CC BY-NC-SA 4.0</a> & <a href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener">MIT</a>'

[menu]
  [[menu.main]]
    identifier = "posts"
    name       = "Articles"
    url        = "/articles"
    weight     = 20
  [[menu.main]]
    identifier = "tags"
    name       = "Tags"
    url        = "/tags"
    weight     = 30
  [[menu.main]]
    identifier = "categories"
    name       = "Categories"
    url        = "/categories"
    weight     = 40
  [[menu.main]]
    identifier = "series"
    name       = "Series"
    url        = "/series"
    weight     = 50
  [[menu.main]]
    identifier = "about"
    name       = "About"
    url        = "/about"
    weight     = 200

[markup]
  [markup.tableOfContents]
    startLevel = 1

If you do not want to call your posts articles as I do, you may replace all articles and Articles you see with posts and Posts. When renaming posts, be sure to keep the identifier as posts1. Setting startLevel to 1 is important to have all sections visible in the table of contents of an article. Also make sure to not add post = "posts" as a taxonomy: it will create duplicate paths and race conditions2.

Having completed the above steps, you can build or preview the website exactly the same way as you did in the quick start, after which you should have a website that looks and behaves very much like mine.

Justification3

If you create a post now, you will see that yours is not justified like this. To achieve justification, set customCSS = ["/style.css"] under [params] in your configuration and paste

.post-content p {
  text-align: justify;
}

into the file static/style.css.

MathJax4

To typeset $\LaTeX$, you can include MathJax in your posts by appending

{{- if .Params.mathjax -}} {{ partial "mathjax.html" . }} {{- end }}

to themes/hello-friend-ng/layouts/partials/head.html and pasting the following into layouts/partials/mathjax.html.

<script
  type="text/javascript"
  async
  src="https://cdn.jsdelivr.net/npm/mathjax@2.7.4/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
></script>
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({
    tex2jax: {
      inlineMath: [['$','$'], ['\\(','\\)']],
      displayMath: [['$$','$$'], ['\[','\]']],
      processEscapes: true,
      processEnvironments: true,
      skipTags: [
        'script', 'noscript', 'style', 'textarea', 'pre', 'code',
      ],
      TeX: {
        equationNumbers: { autoNumber: "AMS" },
        extensions: ["AMSmath.js", "AMSsymbols.js"]
      },
    }
  });
</script>

Then, you should be able to render in-line and display mathematics using $\LaTeX$ syntax respectively between single or double dollar signs after setting mathjax: true in the frontmatter of your post. Here’s an example:

$$ G_{\mu\nu} + \Lambda g_{\mu\nu} = \kappa T_{\mu\nu} $$

MathJax will only be loaded in posts that sets the mathjax field to true. It will not be loaded in any other case, including on non-post pages.

Eliminating JavaScript

hello-friend-ng uses JavaScript for two purposes: to utilise PrismJS and to allow toggling between themes.

If you would like to eliminate the above-mentioned unnecessary usages of JavaScript from your website, which I highly recommend you do, follow these steps:

  • Navigate to themes/hello-friend-ng.
  • Execute rm -rf assets/js layouts/partials/javascript.html.
  • Open layouts/_default/baseof.html in a text editor and remove the line that reads {{ partial "javascript.html" . }}.

Now, your website should be free of JavaScript. If you have set up MathJax, it will still behave as expected.

You can also add enableThemeToggle = false under the [params] section in your configuration to get rid of the now dysfunctional toggle.

Deployment

After building, Hugo will create a directory named (by default) public. Navigate into this directory and execute the following commands to initialise a Git repository, add all files to the Git index and commit them.

git init
git add -A
git commit -m "Initial commit"

This will be the repository you deploy.

Now, you need to create a repository on GitHub that would be acknowledged by the GitHub Pages application. Go onto GitHub and create an empty repository named USERNAME.github.io where USERNAME is your username. Then, switch back to the terminal from before and execute the following.

git remote add origin "git@github.com:USERNAME/USERNAME.github.io.git"
git push --set-upstream origin master

After a few seconds to a few minutes, GitHub Pages will automatically deploy your website on https://USERNAME.github.io.

Alternate Methods

If you would not like to host your website on GitHub Pages, there are many other similar hosting services such as Render. You may use one of those, or even better, host on your own server using your own domain name.

If the exchange rate between your native currency and USD is sufficiently small, hosting should be quite cheap; and, you can get a domain name for free thanks to EU.org.

To learn more about how to host on your own server and set up your own domain name, see the basic courses on LandChad.net.

Automation

Building

To automatically build the website (without drafts) and push it live, paste the following script into .git/hooks/pre-push.

#!/usr/bin/env sh

set -e

cd "$(git rev-parse --show-toplevel)"
hugo --path-warnings
cd public
git add -A
GIT_COMMITTER_NAME="hook" GIT_COMMITTER_EMAIL="" git commit \
    --author="hook <>" \
    -m "#$(($(git rev-list --all --count) + 1)): Automatic build"
git push

If you want the commit to be under your name and be signed normally, replace the commit line with git commit -m "#$(($(git rev-list --all --count) + 1)): Automatic build".

Updating the Last Modification Time

Hugo allows you to set the field lastmod in the frontmatter of your posts. If this field has a value later than the field date, the value is passed onto themes to be displayed somewhere in the post. This value might not be displayed depending on the theme you use.

It is quite cumbersome to update this field manually, so, if you would like to automate it, paste the following script into .git/hooks/pre-commit.

#!/usr/bin/env sh

time="$(date --iso-8601=seconds)"

git diff --cached --name-status \
    | grep "^M" \
    | cut -d'	' -f2- \
    | grep "^content/.*\.md$" \
    | while IFS= read -r file; do
          sed -iE "0,/^lastmod:\s*[0-9]{4}-.*/s//lastmod: $time/" -- "$file"
          git add -- "$file"
      done

  1. Atlialp, D. (2021, August 25). Renaming /posts makes elements disappear. Github. Retrieved August 25, 2021, from https://github.com/rhazdon/hugo-theme-hello-friend-ng/issues/316#issuecomment-905008401↩︎

  2. Pedersen, B. E. (2021, July 16) Build Output Is Indeterministic. HUGO. Retrieved July 16, 2021, from https://discourse.gohugo.io/t/build-output-is-indeterministic/33850↩︎

  3. Mackenzie, D. (2019, May 1). Way to justify posts. GitHub. Retrieved July 15, 2021, from https://github.com/rhazdon/hugo-theme-hello-friend-ng/issues/20#issuecomment-488144749/↩︎

  4. xuchengpeng. (2018, July 10). Setting MathJax with Hugo. xuchengpeng. Retrieved July 15, 2021, from http://xuchengpeng.com/hexo-blog/2018/07/10/setting-mathjax-with-hugo/↩︎