-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
[]
- - About
- - Blog
- - Tags
- - Git
- - Donate
- - []
Making a Semantic Blog Theme
published: Jan 12, 2019
signed with 0x683A22F9469CA4EB
tags: hugo webdev
I am a big fan of the semantic web, POSH markup and microformats. Now
that I’m self-publishing again, one of my first tasks is ensuring my
site generation templates produce well structured, and semantically rich
documents.
It’s important to me that the site work everywhere, especially text
browsers and screen readers. Personally, I do at least half of my web
browsing through a terminal browser, and all of my subscribed content is
consumed through newsboat. It’s dumb when text-based content needs a
javascript capable browser or plugins to display text.
Outlining the content
Obviously we all know what a blog is (you’re literally reading one right
now). In terms of web semantics though, it’s helpful to think about the
structure more formally. So let’s break down exactly what this content
is and what it’s for.
Posts
The primary type of content for this blog is a “post”. The post has two
core elements:
- - title
- - body
These are the things we care most about, and without these the post is
pretty useless.
Posts can also have some useful secondary data. Things like:
- - author
- - date of publication
- - date of updates
- - categorization tags.
The h-entry format is well suited for posts in this format, so that’s
what I’ll be using.
Example markup for a post:
Pages
Pages are like posts without the secondary data. They are functionally
standalone and without any temporal context. As such, they will also use
the h-entry microformat.
In the case of the About page, there’s also an embedded h-card for my
contact info. This h-card will serve as an authoritative semantic
reference for any p-author elements in other content.
Example markup for a page:
Feeds
Feeds (in the context of this blog) are just ordered collections of
posts. There are two types of feeds:
- - posts by date
- - posts by tag
Each will be rendered as an HTML page, as well as an RSS feed.
Markup-wise the feed is more conceptual. It will literally just be a
tag inside the HTML body, filled with repeating post
markup.
Creating the Hugo Theme
As mentioned elsewhere, I use the hugo static site generator. Hugo is
fast, flexible, and generates sitemap and rss feeds so I don’t have to.
The docs are straight forward enough that I’m not going to reproduce
them here. Instead I’m just going to jump in and start creating the
template.
First we run the theme creation command in the shell:
hugo new theme semantic
This generates the following scaffold:
themes/semantic
├── archetypes
│ └── default.md
├── layouts
│ ├── 404.html
│ ├── _default
│ │ ├── baseof.html
│ │ ├── list.html
│ │ └── single.html
│ ├── index.html
│ └── partials
│ ├── footer.html
│ ├── header.html
│ └── head.html
├── LICENSE
├── static
│ ├── css
│ └── js
└── theme.toml
Most of these files are empty placeholders, so we’re just going to
delete them:
find themes/semantic -type f -name "*.html" -exec rm '{}' \;
rm -rf themes/semantic/static/*
Now our directory structure looks like this:
themes/semantic
├── archetypes
│ └── default.md
├── layouts
│ ├── _default
│ └── partials
├── LICENSE
├── static
└── theme.toml
Hugo has its own concept of content “types”, which work really well with
the approach we’ve taken so far. Earlier we defined two semantic types:
pages and posts. Per the docs, we need to make a folder per type inside
of layouts to create these types in hugo:
mkdir -p themes/semantic/layouts/{page,post}
By default, hugo expects a content of a type to be in the type’s named
folder. For example a post would be under content/post/some-post-here.md
and would produce a url of yoursite.biz/posts/some-post-here/. A page
would be under content/page/some-page.md and product a url of
yoursite.biz/pages/some-page/.
In my case, I don’t want to have the page namespace. I want pages to
just be at the site root. For example: https://markhuge.com/about. One
way to do this, is to put the page content files at the root of the
content folder, and explicitly set the type in the front matter.
Creating Content Archetypes
Because pages and posts will have differing front matter, it’s useful to
create archetype templates for each:
Page Archetype:
---
type: "page"
draft: true
---
Post Archetype:
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
type: "post"
author: "Mark"
tags: []
draft: true
---
Here I’ve hardcoded my name as the author, because I’m the only person
contributing to my blog. In the public version of the theme, that line
is omitted.
Creating The HTML Templates
As outlined earlier, we have single html pages for page and post types,
as well as feed pages with lists of posts. Hugo uses a similar
convention of single templates for individual items and list templates
for feeds.
Base template
The baseof.html template is the master template for a type. If no
baseof.html is provided in the type subfolder, the type will use the
baseof.html template from the _default folder. In our case the layout
for most of the site is the same across types, so we’re only going to
specify _default/baseof.html:
{{ partial "header.html" . }}
{{ block "content" . }}
{{ end }}
{{ partial "footer.html" . }}
This is a trivially simple template that specifies the inclusion of a
header template, a footer template, and a block of code labeled
“content”, that will be populated by individual templates below. All
this does is orient “whatever is in the ‘content’ block” between the
header and footer.
Single Post Template
In the layouts/post/single.html template, we populate the content block
used in the baseof.html template.
Here we’re using the h-entry microformat we setup earlier. For the most
part it’s just replacing values with template variables. In the specific
instance of tags, we use the with function to only include the list of
tags if the post has any tags to display:
{{ define "content" }}
// Only display tags list if tags exist on this post
{{ with .Params.tags }}
{{ end }}
{{ .Content }}
{{ end }}
List of Posts Template
The list template layouts/_default/list.html rather than in the post
subfolder. This is because it’s also considered the “home” page, and
hugo will look for the homepage template in _default. We’re populating
the content block the same as we did in the single.html template. In
this case we’re using the h-feed microformat:
{{ define "content" }}
{{ range where .Pages.ByDate.Reverse "Section" "post" }}
{{ end }}
Single Page Template
Pages only have a single layout: layouts/page/single.html. Just as with
posts, we’re populating the content block from the base template. The
page template is also an h-entry, but lacks all the secondary data of a
post, so it’s much simpler:
{{ define "content" }}
{{ end }}
Tags Index Template
Hugo has another type of template called a terms template. A “term” is a
type of category. In our case we’re using tags, so the term template
will be what renders our list of tags. This will be in the _default
layout directory: layouts/_default/terms.html.
Notice this template does not make use of baseof.html, so we need to
include the header.html and footer.html partials directly:
{{ partial "header.html" . }}
{{ partial "footer.html" . }}
Header partial template
The layouts/partials/header.html template is everything above the
content block.
Of particular interest here is the generation of style.css from a sass
file. See “Creating the Stylesheets” further down.
The theme expects normalize.css to be in the static folder of your hugo
site. I did this because I’m not sure what’s appropriate license-wise if
I were to ship the theme with normalize included.
Footer partial template
The layouts/partials/footer.html template is everything below the
content block.
Here we’re making use of a copyright param that is set in the site’s
config file. This is a string that represents whatever name you want to
copyright your content under. In my case it’s my name, but for you it
could be a company or something else.
Creating the Stylesheets
I’m using sass because I want to, and hugo supports it out of the box.
One of the benefits of starting with POSH markup, is that we don’t need
to do a ton of work to style it. We’re not abusing an endless series of