How to Develop a Hugo Theme Component

Contents

This article will demonstrate how to create a Hugo theme component by developing a caniuse shortcode.

Introduction

This article aims to outline the process of developing a component from scratch without delving too deeply into the concept and principles of components. For more information on Hugo theme components, please refer to the Contributing / Develop Theme Components page.

This case study is open-sourced on GitHub under the hugo-fixit organization.

A Hugo theme component with caniuse shortcode.

HTML 2

Creating Component Skeleton

Creating a component is similar to creating a theme, and you can use the hugo new theme command to create a new theme component.

1
hugo new theme shortcode-caniuse

The above command creates a folder named shortcode-caniuse in the themes directory, which has a complete Hugo theme directory structure.

Since we only need to develop a component that includes a shortcode, you can delete unnecessary files. The directory structure after deletion is as follows:

  • shortcode-caniuse/
    • hugo.toml
    • LICENSE
    • README.md
    • theme.toml

The theme.toml file is used for Hugo’s official theme list submission. It is optional for theme components.

To be compatible with both Git submodule and Hugo Modules installation methods later on, you also need to initialize a Git repository and a go.mod file:

1
2
3
git init
git remote add origin git@github.com:hugo-fixit/shortcode-caniuse.git
go mod init github.com/hugo-fixit/shortcode-caniuse

Creating Shortcode

Create a file named caniuse.html in the layouts/_shortcodes directory.

According to the usage instructions of <caniuse-embed> Element, write the shortcode content as follows:

caniuse.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{{- /*
  reference https://github.com/Lruihao/caniuse-embed-element
  @params feature - Feature name
  @params baseline - Whether to show the feature support baseline, default is false
  @params past - Show the past N versions that match the feature, range is 0 - 5, default is 2
  @params future - Show the future N versions that match the feature, range is 0 - 3, default is 1
  @params origin - The origin of the caniuse embed data source, default is "https://caniuse.lruihao.cn"
  @params loading - Loading strategy for the iframe (eager or lazy), default is lazy
*/ -}}
{{- $caniuseEmbed := .Page.Param "caniuse_embed" -}}
{{- $feature := cond .IsNamedParams (.Get "feature") (.Get 0) -}}
{{- $baseline := cond .IsNamedParams (.Get "baseline") (.Get 1) | default $caniuseEmbed.baseline | default false -}}
{{- $past := cond .IsNamedParams (.Get "past") (.Get 2) | default $caniuseEmbed.past | default 2 -}}
{{- $future := cond .IsNamedParams (.Get "future") (.Get 3) | default $caniuseEmbed.future | default 1 -}}
{{- $origin := cond .IsNamedParams (.Get "origin") (.Get 4) | default $caniuseEmbed.origin | default "https://caniuse.lruihao.cn" -}}
{{- $loading := cond .IsNamedParams (.Get "loading") (.Get 5) | default $caniuseEmbed.loading | default "lazy" -}}
<caniuse-embed feature="{{ $feature }}" past="{{ $past }}" future="{{ $future }}" origin="{{ $origin }}" loading="{{ $loading }}"{{ if $baseline }} baseline{{ end }}></caniuse-embed>
{{- /* EOF */ -}}

Creating TypeScript

To keep the caniuse embed in sync with the FixIt theme’s dark/light mode, we use TypeScript to integrate with the FixIt event bus.

Create a file named caniuse.fixit.ts in the assets/js directory:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const { fixit } = window as any

interface CanIUseEmbed extends HTMLElement {
  theme: string
}

function setEmbedsTheme(embedEls: NodeListOf<CanIUseEmbed>, isDark: boolean) {
  embedEls.forEach((el) => {
    el.theme = isDark ? 'dark' : 'light'
  });
}

function initCanIUseeEmbeds() {
  const embedEls = document.querySelectorAll<CanIUseEmbed>('caniuse-embed');
  requestAnimationFrame(() => {
    setEmbedsTheme(embedEls, fixit.isDark)
  })
  fixit.eventBus.on('fixit:switch-theme', ({ detail }: { detail: { mode: string, isDark: boolean, isChanged: boolean } }) => {
    if (detail.isChanged) {
      setEmbedsTheme(embedEls, detail.isDark)
    }
  })
}

document.addEventListener('DOMContentLoaded', initCanIUseeEmbeds, false)

This script listens for the fixit:switch-theme event on the FixIt event bus and updates the theme property on all <caniuse-embed> web components on the page.

Creating Partial

Create a file named shortcode-caniuse.html in the layouts/_partials/inject directory.

This partial loads the <caniuse-embed> web component library and the theme-switching script:

shortcode-caniuse.html
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{{- if .HasShortcode "caniuse" -}}
  {{- $fingerprint := .Site.Store.Get "fingerprint" -}}
  {{- $caniuseEmbedConfig := dict "Page" . "Key" "caniuse_embed" | partial "function/param.html" -}}

  {{- /* caniuse-embed-element.iife.js */ -}}
  {{- $source := resources.Get "lib/caniuse-embed-element.iife.js" -}}
  {{- if hugo.IsProduction | and $caniuseEmbedConfig.cdn -}}
    {{- $source = $caniuseEmbedConfig.cdn -}}
  {{- end -}}
  {{- dict
    "Source" $source
    "Fingerprint" $fingerprint
    "Async" true
    "Defer" true
    | dict "Page" . "Data"
    | partial "store/script.html"
  -}}

  {{- /* caniuse.fixit.min.js */ -}}
  {{- dict
    "Source" "js/caniuse.fixit.ts"
    "Build" true
    "Fingerprint" $fingerprint
    "Defer" true
    | dict "Page" . "Data"
    | partial "store/script.html"
  -}}
{{- end -}}

At this point, the final project structure is as follows:

  • shortcode-caniuse/
    • go.mod
    • hugo.toml
    • LICENSE
    • README.md
    • theme.toml

Publishing Component

Before publishing, update the LICENSE, README.md, and theme.toml files, then commit to the remote repository.

How to Use

Install Component

The installation method is the same as installing a theme. There are several ways to install, choose one, for example, install through Hugo Modules:

1
2
3
4
5
[module]
  [[module.imports]]
    path = "github.com/hugo-fixit/FixIt"
+ [[module.imports]]
+   path = "github.com/hugo-fixit/shortcode-caniuse"

Inject Partial

FixIt 0.3.12 | NEW

In order to inject the partial shortcode-caniuse.html into the custom-assets through the custom block opened by the FixIt theme, you need to fill in the following necessary configurations:

1
2
3
4
5
6
[params]

[params.custom_partials]
# ... other partials
assets = [ "inject/shortcode-caniuse.html" ]
# ... other partials

Configure Site-Level Defaults

You can set site-wide default values for the shortcode parameters under [params.caniuse_embed]:

1
2
3
4
5
6
7
[params.caniuse_embed]
baseline = false
past = 2
future = 1
origin = "https://caniuse.lruihao.cn"
loading = "lazy"
cdn = ""

Shortcode arguments take priority over these site-level defaults.

Use Shortcode

The caniuse shortcode has the following named parameters:

  • feature [required] (first positional parameter) Feature name
  • baseline [optional] (second positional parameter) Whether to show the feature support baseline view, default is false
  • past [optional] (third positional parameter) Show the past N versions that match the feature, range is 0 - 5, default is 2
  • future [optional] (fourth positional parameter) Show the future N versions that match the feature, range is 0 - 3, default is 1
  • origin [optional] (fifth positional parameter) The origin of the caniuse embed data source, default is https://caniuse.lruihao.cn
  • loading [optional] (sixth positional parameter) Loading strategy for the iframe (eager or lazy), default is lazy

Click on the # next to a feature on the caniuse.com website, and the pathname in the URL is the feature parameter.

Example caniuse input:

1
2
3
{{< caniuse feature="mdn-css_types_color_light-dark" >}}
or
{{< caniuse "mdn-css_types_color_light-dark" >}}

The rendered output looks like this:

With baseline view:

1
2
3
{{< caniuse feature="mdn-css_types_color_light-dark" baseline="true" >}}
or
{{< caniuse "mdn-css_types_color_light-dark" true >}}

The rendered output looks like this:

References

Acknowledgements


Related Content

Buy me a coffee
Lruihao AlipayAlipay
Lruihao WeChat PayWeChat Pay

Update Available

A new version of this site is available.