Converting Your HTML One-Pager Into a Hugo Site

I recently set up a blog for a friend of mine. It originated as a single HTML file to demonstrate the concept. In the process of adding support for real posts I learned a few things about hugo.

Make no mistake, I am no Hugo expert, I am not even skilled at it. I just know enough HTML, CSS and ChatGPT to make my way around.

Anyone who has coded anything can understand the massive added complexity of handling edgecases, making things modular and giving the user more choice. Software like the suckless suite are built with this understanding as a core philosophy - that it can be far simpler and easier for both the developer and the end user to modify the code to fit their needs than to accommodate everyone and everything. This is the main reason I chose Lugo as the theme I would start with. I need everything to be as simple as possible so that I can easily plug in my existing HTML.

The changes made to baseof.html are minimal.

Adding a banner:

baseof.html

<nav class="nav">
<a href={{ .Site.BaseURL }}><img class="logo" src="../friendbanner.jpg" alt="author's banner'"></a>
</nav>

Adding a visible title for blogposts:

baseof.html

{{ block "main" . }}
<h1 class="posttitle" id="tag_{{ .Title }}">{{ block "title" . }}{{ end }}</h1>
{{ .Content }}
{{ end }}

Adding a decorative image at the bottom of the site:

baseof.html

{{ block "footer" . }}
<footer>
<div class="bottomcontainer"><a href={{ .Site.BaseURL }}/index.xml><img class="bottomimage" alt="image that recommends RSS" src="../bottomimage.png"></a></div>
{{- if .Param "showrss" }}<br><br><a href="/index.xml"><img src="/rss.svg" style="max-height:1.5em" alt="RSS Feed" title="Subscribe via RSS for updates."></a>{{ end }}
</footer>
{{ end }}

There are some other features I wanted to implement:

To accomplish this, I made a special post in content:

allposts.md

---
title: "friendblog - all posts"
description: "friendblog - all posts"
layout: all-posts
hidden: true
---

We define a custom layout all-posts to use a specific HTML file to generate the list of posts and define a parameter hidden so that we can hide this post from appearing in any lists and RSS feeds. Let’s look at our various HTML templates for lists:

all-posts.html

{{ define "main" }}
{{.Content}}
<h1>{{ .Title }}</h1>
<ul class="all-posts">
{{range .Site.RegularPages}}
{{ if not .Params.hidden }}
<li><a href="{{.RelPermalink}}">{{.Title}}</a></li>
{{end}}
{{ end }}
</ul>
{{ end }}

Notice the {{ if not .Params.hidden }} to hide any posts with the hidden parameter (which includes allposts.md.)

list.html

{{ define "title" -}}
{{ .Title | title }}
{{- end }}
{{ define "main" -}}
{{ .Content }}

{{ if .Site.Home }}
<ul>
{{- range.Pages | first 5}}
{{ if not .Params.hidden }}
<li>
	{{- if .Param "datesinlist" }}<time datetime="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}">{{ .Date.Format "2006 Jan 02" }}</time> &ndash; {{ end -}}
	<a href="{{ .RelPermalink }}">{{ .Title }}</a>
	{{- if .Param "authorsinlist" }}
	{{ with .Params.authors }}
		by
		{{ delimit . ", " " and " }}
	{{end}}
	{{ end -}}
</li>
{{ end }}
{{- end }}
</ul>
<a href="./allposts"><div class="center">see all posts...</div></a>

{{ else }}

<ul>
{{- range.Pages }}
{{ if not .Params.hidden }}
<li>
	{{- if .Param "datesinlist" }}<time datetime="{{ .Date.Format "2006-01-02T15:04:05Z07:00" }}">{{ .Date.Format "2006 Jan 02" }}</time> &ndash; {{ end -}}
	<a href="{{ .RelPermalink }}">{{ .Title }}</a>
	{{- if .Param "authorsinlist" }}
	{{ with .Params.authors }}
		by
		{{ delimit . ", " " and " }}
	{{end}}
	{{ end -}}
</li>
{{ end }}
{{- end }}
</ul>
{{- end }}
{{ end }}

The important part here is the first list, which says “if we are home, for each page, but only the first 5, make a list yada yada.” Else, we do the same thing but without the “| first 5” part.

I’m sure I could add the if statement in the “first 5” part and avoid two huge blocks of HTML but who cares, it works and it gives me more flexibility down the road!

Adding the hidden feature to avoid hidden posts showing in RSS feed:

rss.xml

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
	<title>{{ .Site.Title }}</title>
	<link>{{ .Permalink }}</link>
	<description>Recent content {{ if ne  .Title  .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}</description>
	<generator>Hugo -- gohugo.io</generator>{{ with .Site.LanguageCode }}
	<language>{{.}}</language>{{end}}{{ with .Site.Author.email }}
	<managingEditor>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</managingEditor>{{end}}{{ with .Site.Author.email }}
	<webMaster>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</webMaster>{{end}}{{ with .Site.Copyright }}
	<copyright>{{.}}</copyright>{{end}}{{ if not .Date.IsZero }}
	<lastBuildDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</lastBuildDate>{{ end }}
    {{ with .OutputFormats.Get "RSS" }}
        {{ printf "<atom:link href=%q rel=\"self\" type=%q />" .Permalink .MediaType | safeHTML }}
	{{ end }}
	{{ range .Site.RegularPages }}
		{{ if not .Params.hidden }}
	<item>
		<title>{{ .Title }}</title>
		<link>{{ .Permalink }}</link>
		<pubDate>{{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}</pubDate>
		{{ with .Site.Author.email }}<author>{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}</author>{{end}}
		<guid>{{ .Permalink }}</guid>
		<description>{{- .Content | html -}}</description>
	</item>
	    {{ end }}
	{{ end }}
	</channel>
</rss>

And finally, the main page that I originally wrote as a single HTML file is named _index.html and is saved in the root of content. I’m not totally sure if it should go here or in layouts to create a “home” page but it seems to work fine.

Misc scripts that I am using with this site to make it easier for my friend:

makejpg.sh

Loops through all images inside ./static/images, resizes them so that both height and width are less than or equal to 1000px, compresses them to 70% of their original quality, converts them to jpg if they are not already, if they were converted to jpg, delete the original file.

#!/bin/bash

# Directory to work in
cd ./static/images
# Loop through all image files in the directory using find
find . -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" -o -iname "*.bmp" -o -iname "*.tif" -o -iname "*.tiff" \) |
while read -r image_file; do
    # Scale the image so that both X/Y dimensions are less than or equal to 1000px using ImageMagick
    mogrify -resize "1000x1000>" "$image_file"

    # Compress the jpg image to 70% quality using ImageMagick
    mogrify -quality 70 "$image_file"

    # Check if the file type is jpg
    if [ "${image_file##*.}" != "jpg" ]; then
        # Convert the image to jpg using ImageMagick
        mogrify -format jpg "$image_file"

        # If it was converted, delete the original image file
        rm "$image_file"
    fi
done

cd

fixperms.sh

I have observed MEGAsync messing up file permissions which lead to many issues. This fixes that.

#!/bin/bash

# Define the desired group permissions for files, directories, and shell files using symbolic notation
FILE_PERMS="ug=rw,o=r"
DIR_PERMS="ug=rwx,o=rx"
SH_PERMS="ug=rwx,o=rx"

# Set group permissions recursively on files, directories, and shell files separately
find . -type f -name "*.sh" -exec chmod $SH_PERMS {} +
find . -type f ! -name "*.sh" -exec chmod $FILE_PERMS {} +
find . -type d -exec chmod $DIR_PERMS {} +

echo "Group permissions set to $FILE_PERMS on files, $DIR_PERMS on directories, and $SH_PERMS on shell files recursively."

deploy.sh

Runs the two scripts mentioned previously, builds the site with hugo, then rsyncs it to a VPS.


#!/bin/bash
USER=root
HOST=FRIENDSITE.COM
DIR=/var/www/FRIENDSITE.COM/
./makepng.sh
./fixperms.sh
hugo && rsync -azvh --exclude='.DS_Store' --delete public/ ${USER}@${HOST}:${DIR} && rm -rf public

exit 0
Previous:
Apple Hates Your Webapp and You Will Own Nothing
Next:
Sunshine and Moonlight - Airplay for your Linux Desktop
Related
Computers