Until recently I built my personal webpage with Nikola (see this post). However I decided to change that when moving to GitHub pages, in part because I thought Jekyll would be easier to use on GitHub, in part because after a Nikola upgrade the theme I was using broke and didn't know how to fix it. The problem is probably ignorance on my part more than anything else. I am no expert on web publishing and can't afford to become one, I just need to put up a decent website (and I hate CMSs).
However, reading Jekyll documentation scared me a bit, and a dear colleague pointed me to Hugo. I followed his suggestion, and I managed to set up a rather nice, if not perfect, website. Now that it's done, it seems I have a setup where I can easily enough create and update content in a bunch of plain text files (stored in GitHub), quickly create a site and publish it through GitHub pages. However, I'd lie if I said setting it up was a breeze. I am certainly no web expert, but I have been around computers for a while (my first encounter with them was through my father's TRS-80 Model I), and this is not my first time with a static web site generator. Yet I had a hard time at first.
So I decided to write this summary of my experience and what I gather about how Hugo works. It will serve mainly for my own reference, but it might also be useful to others, so I publish it here. When the Hugo docs failed me (which at first was very often), I found much help in this post from Sara Souiedan's blog, and in this one by J. P. Droege.
Understanding Hugo
My main problem with Hugo is documentation. Not the not lack of documentation: there is plenty of it. However, it seems designed to explain Hugo a detail at a time, and there is no "high-level" or big-picture summary of how Hugo works, and how a source directory structure is turned into a web site. There are also very detailed guides to install and work with a particular theme. In fact I started with one of those, which is fine if you plan to use the theme exactly as it comes. However, as soon as I wanted to tweak a minor detail, I stumbled due to lack of understanding of how Hugo interprets your source. What follows is what I eventually gathered, from the mentioned blog posts, the docs, and a lot of trial and error.
This is not intended as a guide to use Hugo to build a complete web site from scratch. I don't know enough to do that, and I am using a theme, which fills in the CSSs and other stuff needed to get a nice-looking site. I am just trying to get enough understanding of Hugo's workings to be able to do good use of a theme (note again that I'm not an expert and I could be wrong in what I say, and that I will be happy to stand corrected if someone with more knowledge cares to leave a comment).
After installing Hugo (I did so through my Linux distro package
manager), one creates a bare working tree (starting at, say,
./test/
) with the directory structure Hugo expects with
hugo new site test cd test
Essentially, Hugo will create a site in the public/
directory by
applying a series of transformations to the content of the content/
directory. These transformations involve various things I don't
understand completely, but basically Hugo reads your content from a
source tree starting at content/
, plus a set of templates (which you
write or get as part of a theme) and a set of data (defined in the
header of the source file themselves or in various configuration
files) and writes .html
files to pulibc/
. Additional files needed
for the site (like images and .css
) are copied as-is from the
static/
directory. The templates (though Hugo does not call them
that, but layouts or archetypes) and other stuff that formats your
content (and in principle does not change as often) are in
archetypes/
and layouts/
.
The site is created when you invoke hugo
on the root of your source
site. To draft and debug the site, you can call hugo serve
and your
site is served locally, and automatically updated upon any source tree
change (and very fast). This is a great feature.
Source files are normally written in some markup language that makes
your life easy. Markdown (.md
) seems to be the standard for
Hugo. I'm a fan of Emacs and Orgmode, and luckily Hugo supports that
too, probably along with others I know nothing about (orgmode
users
may find better to use ox-hugo, which exports orgmode
to
Hugo-tailored Markdown, but I'm not doing this so far). These source
files typically start with a header ("front matter") that defines
variables (aka metadata) that help Hugo decide how to process the file
and supply information like date or title for the page. This header
basically consists in key, value pairs and is written in toml
,
yaml
, json
(like the configuration files). I don't really know
any of these, but I use toml
which is quite intuitive and easy to
pick up from the examples. In the case of .org
files, it is also
possible to use a syntax similar to org
properties.
In principle, every file in content/
becomes a page, but I do not
understand all the rules about the process. What I can say is that
content/_index.md
(or content/_index.org
, in all cases I've tried,
.org
files work just as well as .md
files, I will not mention this
equivalence again) produces your home, or landing, page. But for this
to happen, Hugo needs an appropriate template (or layout) file in
layouts/
. For the home page, the appropriate layout is
index.html
. You don't get a page if there is no layout. And,
probably because the home page is special, the index.html
turns out
to be processed even if there is no _index.md
.
I've succeeded in getting a home page using the following two files:
A content/_index.org
file:
#+Title: "My nice home page"
* I write in Emacs
And this is my nice =_index.org= file.
and a layouts/index.html
file:
<header> The page title is {{ .Title }} </header>
And more layout stuff...
{{ .Content }}
As you see, the layout file is written in a mixture of html
and
stuff between braces that calls in content (and executes functions).
To quote the documentation, "Hugo uses Go’s html/template
and
text/template
libraries as the basis for the templating." This is
all I know, but it has proved enough to make small tweaks to the
layouts provided by the themes.
Now let's try a regular static page. Create a normalpage.org
file
in content/
with a single-line content. To produce a page from this
file, again Hugo needs a template (if you run hugo
at this point,
Hugo will complain found no layout file for "HTML" for kind
"page"
). There are certain rules to match a source file to a
template, and they depend on the kind of the page. I haven't
completely figured out the rules to establish the kind of the page.
Apparently, content/_index.md
is a kind of it's own. The other
pages in content/
(but not in subdirectories, see below) are, at
least by default, of kind "page", and the corresponding layout is
layouts/_default/single.html
. Putting in this file just
{{ .Content }}
I got the page to be served at localhost:1313/normalpage/
. Now I
can link to it from the home page:
a [[./normalpage][nice]] link
I found I could get Hugo to tell me what kind it is assigning to pages by adding this line to each layout:
My kind is {{ .Kind }}
This turned out to be very useful; this way I learned that the home
page (_index.org
) is of kind home
, and confirmed that
/content/normalpage.org
is of kind page
.
As a next level I add a subdirectory /content/blog
and again a
one-liner something.org
file. Now Hugo complains about lack of
layout for kinds "section" and "taxonomyTerm". The page
http://localhost:1313/blog/
appears empty, although
http://localhost:1313/blog/something
displays my one-liner (which
turns out to also be of kind page
).
Adding an index.org
page in content/blog
makes content appear on
blog/
(of kind page
, i.e. using our single.html
template). If
instead you name the file _index.org
, then it is interpreted as of
kind section
, and processed with a list.html
layout. Normally
this layout is written so that it displays a nice table of contents,
or summary page, of all the files contained in the directory (which
will be in any case translated to pages, but now they are
automatically linked to). I don't know how to write a template like
this, but I don't need to since I will be using a theme anyway (you
can see an example in Sara's post). Apparently, if you include neither
index.md
nor _index.md
, Hugo assumes an empty _index.md
anyway
(as in the case for the home page) and tries to read the list.html
template, which would explain why it complained about the lack of a
section layout when I put only a single file in blog
.
Up to now I have placed the layouts under _default
, but if you put
layouts in layouts/blog
, then Hugo will use those instead when
reading content/blog
. I also succeeded in using the blog/
layouts
from another subdirectory by setting
#+type: blog
in the front matter.
Armed with this basic knowledge (admittedly sketchy and incomplete), I felt confident enough to finally download a theme and start tweaking it to my taste.
Installing a theme
I decided to try the Academia Hugo theme:
hugo new site personal
cd personal
git init .
cd themes/
git submodule add https://github.com/themefisher/academia-hugo.git
Themes come with their set of layouts and static stuff that gives the
site the final appearance. Hugo can get the layouts directly from the
theme directory instead of using your layouts/
directory. To use
the theme you need to point Hugo to it from the configuration
parameters. These are written as toml
, yaml
or json
files, either
in the single file config.toml
or in several files in
config/_default
. I follow the second approach.
mkdir -p config/_default mv config.toml config/_default/
Adding the line
theme = "academia-hugo"
makes Hugo aware of the theme.
Academia Hugo "hijacks" the home page (and this caused me a lot of
confusion at first): its home layout (i.e. layouts/index.htm
)
ignores any content you care to write in _index.md
and instead
includes a widget_page.html
layout which looks for content in
content/home
(including layout files within another is apparently
called working with partials in Hugo jargon). So we must start
populating this directory. But first, note that I am building a
dual-language site. Hugo supports this; essentially by placing the
source for each language in parallel trees in content/
(and setting
some configuration variables). So I create the directories for
languages:
mkdir -p content/en/home mkdir -p content/es/home
Now copy the configuration files config.toml
, languages.toml
and
params.toml
from the theme's exampleSite
directory and start
changing the parameters. In particular add in config.toml
,
defaultContentLanguage = "en"
hasCJKLanguage = false # Set `true` for Chinese/Japanese/Korean languages.
defaultContentLanguageInSubdir = false
removePathAccents = true # Workaround for https://github.com/gohugoio/hugo/issues/5687
[languages.en]
languageCode = "en-us"
contentDir = "content/en"
[languages.es]
languageCode = "es-ar"
contentDir = "content/es"
Actually the language part I put in a different file, called
languages.toml
, which has the advantage that you can say [en]
instead of [languages.en]
at the beginning of the section, because
all the contents of languages.toml
are interpreted as if defined
inside a [languages]
section.
Now place an empty index.md
in each language home/
. At this point
hugo serve -D
displays a site with a header and footer but otherwise
empty.
Populating the home page
The home (or landing) page is made from "widgets", which can be copied
and tweaked from the theme's example site. Many widgets completely
ignore any content and work only from the front matter, taking custom
data from various parameter files. I try to avoid this approach,
which resembles too much a CMS, but I accepted it for the about.md
widget. I copied it from the example site to each of the home/
directories. To work, this widget requires data stored in
home/authors/admin/_index.md
. Copy and edit from the example site;
after the front matter, text can be added in .md
format that will be
displayed by the widget.
I added an intro (first.md
) by copying from the blank widget. Note
that the weight
parameter is used to select the order in which the
widgets appear.
Menu
The theme comes with a menu, adding to it is done through the
configuration files. You can follow the theme's example, only that
for multilingual sites the definitions must go in the [languages]
section (or langauges.toml
file):
[[en.menu.main]]
name = "Home"
url = "#first"
weight = 10
[[es.menu.main]]
name = "Home"
url = "#first"
weight = 10
etc.
The teaching page
I want a static "subsite" rendered from a source tree starting at
/es/docencia
(in the Spanish branch). I want to put each course in
a separate subdirectory so that material for different courses will be
cleanly organized in source. Hugo allows this, but it appears that if
you put an index.md
file in content/es/docencia
, the
subdirectories are ignored. If you put a _index.md
file, or
nothing, subdirectories are read, and a summary page is generated.
However, I didn't like the automatic summary page and I wanted to
write my own. I haven't figured out how to avoid the automatic
summary page, so I ended up writing my own in
es/docencia/docencia.org
. This page appears at
es/docencia/docencia
, and the automatic summary at es/docencia
. I
simply avoided any links to the automatic summary, and link directly
from the menu to es/docencia/docencia
. The default type for these
pages seems to be post
, which causes the theme to output a signature
at the bottom (a bit redundant, since this is a personal site). I
avoided this by setting the type to page
in the front matter:
#+title: Docencia
#+type: page
#+date: 2020-07-17
I could easily add PDF files with course material and link to them
just placing them alongside the .org
files in content/
.
Blog
The blog part worked out of the box by creating entries in a posts
subdirectory. You can use one file per post, or one directory per
post (useful I think to attach images and such). In the last case,
the name of the directory becomes the link, and you write in
index.org
. The summary is handled automatically. Of course, a link
in the menu or somewhere else is needed for readers to find the blog.
Conclusion
There are still things to do to get the site I want (like adding a
publication list created from my BibTex
files, something I have yet
to figure out). But I've succeeded in setting up a decent site, and
after the initial difficulties, producing and publishing content with
Hugo is proving a good experience.