Wilson E. Husin

Underrated Hugo features

December 11, 2022 (UTC)

The site you are reading now is generated by Hugo (oh, and the source code for this site is open-source). I have had a generally pleasant time managing Hugo-based static site for the last few years.

I also found that it has plenty of hidden gems (wink Jekyll wink) — some really should have been the default behavior.

Image resizing (and WebP conversion)

Hugo has built in support for resizing and WebP conversion, which can be invoked by calling .Resize. Here is a sample from the documentation about image processing:

{{ with .Resources.GetMatch "sunset.jpg" }}
  {{ with .Resize (printf "%dx%d webp" .Width .Height) }}
    <img src="{{ .RelPermalink }}" width="{{ .Width }}" height="{{ .Height }}">
  {{ end }}
{{ end }}

Refer to documentation on Page Resources to learn more about the .Resource usage.

Markdown render hooks

By default, links generated by Hugo will open in the same browser page. However, Hugo allows you to customize it through invoking Markdown render hooks and add the target="_blank" conditional yourself. I think this deserves to be default behavior.

<!-- layouts/_default/_markup/render-link.html -->
<a
  href="{{ .Destination | safeURL }}"
  {{ with .Title }}title="{{ . }}"{{ end }}
  {{ if strings.HasPrefix .Destination "http" }}
    target="_blank" rel="noopener"
  {{ end }}
  >
  {{- .Text | safeHTML -}}
</a>

Additionally, Hugo also supports overriding # conversion to <h1> (and its variances). Useful for adding anchors.

Truncating extra line / space

If you use Hugo shortcodes for inline text, you may notice an extra space or newline being added automatically after the shortcode invocation. This is typically a problem in *nix-based systems where every file ends with newline, and Hugo process them literally.

To mitigate this, add dashes within your shortcode invocation, i.e. {{- myshortcode -}} as Hugo honors Go’s templating behavior. Alternatively, you may also append the end of shortcode template file with {{- print "" -}}.

Dynamic syntax highlighting

Hugo internally relies on Chroma to add syntax highlighting on codeblocks. Conveniently, Chroma comes with plenty of popular syntax highlighting themes. Chroma also provides a playground though the options are not exhaustive of all available themes / styles. The fact that such a feature is built in means we don’t need to invoke something like PrismJS.

Now here is the tricky part: if your site supports dark and light mode, a given syntax highlight color may only play along with one mode but not the other. At the cost of a few more kilobytes being transferred over the network, you can extract the syntax highlighting system to a CSS file, then set markup.highlight.noClasses = false in config.toml.

For example, this site uses gruvbox theme, where it’s more friendly to dark mode pages. Chroma also has gruvbox-light for the light mode counterpart. HTML allows conditional load of different assets based on prefers-color-scheme, which allowed me to load different Chroma styles.

 1<head>
 2  <link
 3    rel="stylesheet"
 4    href="/css/syntax/gruvbox-dark.css"
 5    media="(prefers-color-scheme: dark)"
 6  />
 7  <link
 8    rel="stylesheet"
 9    href="/css/syntax/gruvbox-light.css"
10    media="(prefers-color-scheme: light)"
11  />
12</head>

Remember to update markup.highlight.noClasses on config.toml.

[markup]
  [markup.highlight]
    noClasses = false