/til/

􀀂􀀟􀀍􀀆 􀀂􀀝􀀃 Shortcodes and outputs

In Hugo, you can make the same shortcode do different things for different outputs.

Prerequisite concepts:

  1. Hugo can output content in multiple formats; by default the site outputs HTML and RSS. The different formats use different templates inside the layouts/ directory. It’s possible to define your own custom output formats and have Hugo output them, too.
  2. Hugo shortcodes are snippets that call templates in your content. For instance, Hugo defines the ref shortcode which gets the URL for content, such as (from the documentation) [Post 1]({{% ref "/posts/post-1" %}}) being rendered as <a href="http://example.org/posts/post-1/">Post 1</a>. You can also write your own shortcodes to output whatever HTML you want.

So, the punchline is that you can write your own shortcodes and have them behave differently when making different outputs.

Why would you want to do this? I use this in a couple of key places for HTML that looks great on the web but doesn’t render properly in RSS. Feed readers don’t use a site’s CSS at all, but most of them will apply very limited styles that are inlined as HTML style=... attributes. Sometimes this can make a real difference.

For example, I have a shortcode called callout that works like this:

On the web, this is styled with CSS to show a border and a background color so that it stands out from the rest of the content. Since RSS doesn’t read my CSS, I have a different version of the shortcode that inlines just the border, so that the contents of the callout box don’t get mixed up with the main page content. That isn’t very reliable, though — some1 feed readers don’t even apply the inlined border. For that reason I also insert an <hr/> element before and after the <aside>. I don’t bother trying to inline the background color, both because I can’t rely on it, and because I don’t know if the user’s app will be in dark mode or light mode, and I suspect it won’t be possible to add a single background color that would look good behind either white or black text. It’s OK with me that the RSS is much simpler than the HTML, I just want to make sure the content is clear in RSS.

Source code

The only difference between these two files is the style="..." attribute in the RSS template

layouts/shortcodes/callout.html

{{/* A callout is a colored box that stands out from the rest of the text.
   *
   * Arguments:
   *   type: The style of the box, one of "tip", "warning", "none". Default: "none"
   *   header: The text to display in the box header. Default: ""
   *
   * TODO: Don't support a header argument, just let the caller put a header in the content.
   */}}
{{- $calloutType := default "none" (.Get "type") }}
{{- $calloutTypeClass := printf "callout-%s" $calloutType }}
{{- $header := .Get "header" }}
<aside class="callout {{ $calloutTypeClass }}">
  {{ with $header }}<h3 class="callout-header">{{ $header | markdownify }}</h3>{{ end }}
  {{ .Inner | markdownify }}
</aside>

layouts/shortcodes/callout.rss.xml

{{/* A callout is a colored box that stands out from the rest of the text.
   *
   * Arguments:
   *   type: The style of the box, one of "tip", "warning", "none". Default: "none"
   *   header: The text to display in the box header. Default: ""
   *
   * TODO: Don't support a header argument, just let the caller put a header in the content.
   */}}
{{- $calloutType := default "none" (.Get "type") }}
{{- $calloutTypeClass := printf "callout-%s" $calloutType }}
{{- $header := .Get "header" }}
<hr />
<aside style="border: 1px solid gray; margin: 1em; padding: 1em;" class="callout {{ $calloutTypeClass }}">
  {{ with $header }}<h3 class="callout-header">{{ $header | markdownify }}</h3>{{ end }}
  {{ .Inner | markdownify }}
</aside>
<hr />

To make this work, you just need to make two shortcode files and name them according to the template lookup order. For this shortcode, I use

  • layouts/shortcodes/callout.html
  • layouts/shortcodes/callout.rss.xml

(I am happy to have helped improve the template lookup order documentation.)

You can also use this to write blocks that only appear on the web and not in RSS, or vice versa.

Updates

  • 2024-09-01: Add <hr/> to the callout box in RSS.

  1. Feed readers are very inconsistent on this. I originally tested this shortcode in NetNewsWire on the Mac, which I can do locally without deploying the site, and it displayed the border just fine. That was several weeks ago by now, and I didn’t realize that NetNewsWire on iOS doesn’t actually display the border! After making this post and seeing the result on iOS, I added the <hr/> elements. ↩︎

Responses

Webmentions

Hosted on remote sites, and collected here via Webmention.io (thanks!).

Comments

Comments are hosted on this site and powered by Remark42 (thanks!).