Migrating an old static HTML site to Hugo
Today was an exciting day of web development as I began the process of migrating my old HTML-only website to Hugo, a modern static site generator. What started as a simple migration turned into a deep dive into Hugo’s architecture, the Blowfish theme, and some interesting technical problem-solving.
Setting Up Hugo#
Next, I created a new Hugo site using the standard command:
hugo new site newsite
This created the basic Hugo structure with directories for content, layouts, static assets, and configuration files.
Starting the Migration#
The day began with fetching my old website from the server using rsync and organizing it into the _archive
directory. This gave me a clean starting point while preserving the original content for reference.
rsync -avz user@server:/path/to/old/site/ _archive/
Content Migration Challenges#
The first major task was converting individual HTML pages into Markdown format for Hugo’s content directory. Using Cursor (my AI-powered editor), I began extracting meaningful content from the HTML files and translating them into clean Markdown.
Please extract the meaningful content from this HTML page and convert it to a markdown page.
This process revealed an interesting challenge: the LLM was converting our old grid-based gallery layouts into simple rows of text, losing the visual appeal of the original design.
Building a Gallery Display#
To address the gallery issue, I started building a simple flexbox/Tailwind gallery layout. I used prompts like:
“Please create a simple flexbox/tailwind gallery page layout”
This approach worked well for basic layouts, but I soon discovered that the Blowfish theme I was using already had built-in gallery components.
Discovering Blowfish Theme Features#
The Blowfish theme for Hugo turned out to be quite feature-rich. I found that it already had gallery functionality built-in, which saved me significant development time. Converting the existing galleries to use the theme’s components was much more efficient than building custom solutions.
Case Studies Page Development#
One of the more complex challenges was replicating our Case Studies page. I wanted to create visually appealing article cards with featured images for each case study. This required significant time reading both the Hugo documentation and the Blowfish template code to understand how to implement this properly.
Golang pattern matching, Hugo and Blowfish complexity#
A crucial discovery was how Blowfish, and Hugo handle featured images for posts/articles. The correct approach is to:
- Create a directory with the article/post title
- Place an
index.md
file inside that directory - Include a image with the filename
featured.png
same directory.
With Hugo, you can easily access sibling files of your post in templates, and this feautre is used by the Blowfish theme to find all images related to the post, then filter using pattern matching for any images matching the pattern *feature*
.
Blowfish has a partial template which uses the pattern *feature*
to find the featured image for a linked page Seen here in Github partials/article-link/simple.html. I was going slightly loony trying to understand why this was not matching my featured image path assets/case-studies/company-x/featured.png
.
Pattern vs “globstar” matching in Go (and probably everything else)#
The issue I encountered comes from how each pattern treats directory separators (/).
-
The single-asterisk (
*
) is a wildcard that matches any sequence of characters, except for the path separator. When the pattern*feature*
is tested against/content/case-studies/company-x/featured.png
, the matcher looks for<some character>feature<some character>
, but does not consider/
a character so we get no match -
The double-asterisk (**), often called a “globstar,” that matches any sequence of characters, including path separators. From some light Googling, it seems that Go’s built-in path/filepath.Match function does not support the globstar
**
pattern, but you can use
However, this pattern wouldn’t match images in subdirectories like content/case-studies/company-x/featured.png
. The solution was to add an extra wildcard:
{{- $featured := $images.GetMatch "**feature*" -}}
Writing blogs with LLM support#
I feel like I should add a section about this because I find using a combination of Claude and Gemini to flesh out the number of paragraphs for a blog post is a workable solution in mid-2025. The prose they generate is so awful, though, so I end up rewriting nearly everything.
I want to capture the key insights so I remember them in 6 years after no one is still using Hugo, static sites, and maybe the internet has been turned off. I take a lot of notes with Obsidian, but turning those into usable blog posts that I’m okay with publishing is a bit more work and takes a lot of effort.
shrug
Next Steps#
Moving forward, I plan to:
- Work with my client on different approaches for self-publishing
- Git-based workflow using Github/Gitlab actions
- Getting go/Hugo running on their computer and pushing to production :boom:
- Look into creating a PR for Blowfish to use
**
pattern matchin