OVERHEAD

Being resource­ful

Deciphering Hugo

Quick refresher

  • Page resources: This is content located under bundles, e.g. content/blog/post/header.png. This is handled by the .Resources function.
  • Site resources: This is content located under assets/, e.g. assets/favicon-for-processing.png. This is handled by the resources function (notice the lack of a dot, since it’s always global and can’t have a different context).

Now that that’s out of the way, let’s get to the practical stuff…

Override Stylesheets

Sometimes, you want to uniquely style a single blog post, without needing to modify the main stylesheet, and you want to inline the styling into the HTML since the changes are usually pretty minimal. This is easy to achieve with page resources:

HTML snippet for layouts/partials/header.html, using the .Resources.Get function.

<head>
	...

{{ with .Resources.Get "override.css" }}
	<style>{{ safeCSS .Content }}</style>
{{ end }}

	...
</head>

When ./override.css exists, the <style> element is included, with the stylesheet’s content inlined into it (safeCSS marks the content as safe to use for styling, see the safeCSS function’s documentation for more information), and this works for all bundles (e.g. content/blog/override.css).

Inlined favicons

If your favicon is really small, and you’re looking to reduce the number of individual requests being made by your website (or just because you think it’s neat), you can inline it into your HTML as base64-encoded content. This is possible with site resources:

HTML snippet for layouts/partials/header.html, using the resources.Get function.

<head>
	...

{{ with resources.Get "/favicon.png" }}
	<link rel="icon" type="{{ .MediaType }}" href="data:{{ .MediaType }};base64,{{ base64Encode .Content }}">
{{ end }}

	...
</head>

Whenever you change the image for assets/favicon.png, the new favicon will be inlined automatically into the <link> element, saving the extra favicon request in most cases. However, you should include favicon.png under static/ as well, with a <link> element that points to it, because a lot of RSS readers can’t parse inlined images.

Hugo variables in stylesheets

Yes, you read the description right, you can in fact put Hugo variables inside of stylesheets, and all it takes is copying the stylesheet from assets/ with the right functions. But first, here’s how resources copying normally works:

Example of using the resources.Copy function to copy a file to a new destination.

{{ (resources.Get "before.css" | resources.Copy "/after.css" ).Publish }}
  1. resources.Get fetches assets/before.css.
  2. resources.Copy copies it to /after.css, which is under the homepage.
  3. .Publish executes the operation.

The problem with resources.Copy is that it won’t expand any Hugo variables inside before.css; it just copies it as is. But, let’s say before.css contains this:

CSS for assets/before.css, containing a .Title variable.

element::before { content: '{{ .Title }}'; }

To copy before.css to after.css so that .Title is expanded, we can replace resources.Copy with resources.ExecuteAsTemplate, which would look like this:

Example of using the resources.ExecuteAsTemplate function to copy a file to a new destination.

{{ (resources.Get "before.css" | resources.ExecuteAsTemplate "/after.css" . ).Publish }}

Unlike resources.Copy, resources.ExecuteAsTemplate takes two arguments:

  • The first argument is the path to the destination.
  • And the second argument is the context passed to the template, much like how you’d specify {{ partial "example.html" . }} to pass the dot as the partial’s context.

In this case, after.css is copied under the homepage, so .Title will be expanded to the homepage’s .Title value.

My use-case

I personally use this method to create count.css files under each series term, where each stylesheet sets --total-pages: {{ .Pages.Len }};, which avoids needing to update the HTML of all posts within a series whenever the total number of pages changes. Here’s what the copying for that looks like:

HTML snippet of layouts/_default/list.html, which executes the resources.ExecuteAsTemplate function when the current page’s parent has a lower-cased title of “series”.

{{ if eq (lower .Parent.Title) "series" }}
	{{ (resources.Get "count.css" | resources.ExecuteAsTemplate (path.Join page.RelPermalink "count.css") . ).Publish }}
{{ end }}

Additionally, the header partial is modified so the count.css stylesheets are sourced whenever a post is apart of the relevant series:

HTML snippet of layouts/partials/header.html, which ranges over all series terms, and creates a <link> element for each term, sourcing its count.css stylesheet.

<head>
	...

{{ range .Params.series }}
	{{ $termpage := page.Site.GetPage "series" (urlize .) }}
	<link rel="stylesheet" type="text/css" href="{{ path.Join $termpage.RelPermalink "count.css" }}">
{{ end }}

	...
</head>

In my actual partials, I additionally condition the count.css creation and sourcing on whether the number of pages under a term is greater than one, since, for what I use the information for, it doesn’t give me anything useful.

Which looks like: if and (eq (lower .Parent.Title) "series") (gt .Pages.Len 1), and if gt $termpage.Pages.Len 1 (inside the range .Params.series function).