How to Develop a Hugo Theme Component

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.

一个含有 caniuse shortcode 的 Hugo 主题组件。

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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
./
├── assets/
├── i18n/
├── layouts/
│   ├── partials/
│   └── shortcodes/
├── hugo.toml
├── LICENSE
├── README.md
└── theme.toml

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 The CanIUse Embed, write the shortcode content as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{{- /* 
  reference https://github.com/pengzhanbo/caniuse-embed
  <feature>: Feature name
  <past>: Show the past N versions that match the feature, range is 0 - 5, default is 2
  <future>: Show the future N versions that match the feature, range is 0 - 3, default is 1
*/ -}}
{{- $feature := cond .IsNamedParams (.Get "feature") (.Get 0) -}}
{{- $past := cond .IsNamedParams (.Get "past") (.Get 1) | default 2 -}}
{{- $future := cond .IsNamedParams (.Get "future") (.Get 2) | default 1 -}}

<p class="ciu-embed" data-feature="{{ $feature }}" data-past="{{ $past }}" data-future="{{ $future }}" data-observer="true" data-theme=""></p>
{{- /* EOF */ -}}

Creating JS File

In order to change the theme color of the FixIt theme to black and light, we can also change the theme color of the shortcode by using JS.

Create a file named shortcode-caniuse.js in the assets/js directory and write the theme switch logic as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function setCanIUseEmbedsTheme(allCanIUseEmbeds, isDark) {
  allCanIUseEmbeds.forEach(function (embed) {
    embed.setAttribute('data-theme', isDark ? 'dark' : 'light');
  });
}

function CanIUseShortcodeInit() {
  if (typeof window.fixit?.switchThemeEventSet === 'object') {
    const allCanIUseEmbeds = document.querySelectorAll('.ciu-embed');
    setCanIUseEmbedsTheme(allCanIUseEmbeds, window.fixit.isDark);
    window.fixit?.switchThemeEventSet.add(function (isDark) {
      setCanIUseEmbedsTheme(allCanIUseEmbeds, isDark);
    })
    return;
  }
}


if (document.readyState !== 'loading') {
  CanIUseShortcodeInit();
} else {
  document.addEventListener('DOMContentLoaded', caniuseShortcodeInit, false);
}

Creating Partial

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

Reference third-party plugins and the JS resources of the component itself, with the content as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{{- if .HasShortcode "caniuse" -}}
  {{- $fingerprint := .Scratch.Get "fingerprint" -}}

  {{- /* embed.js */ -}}
  {{- $source := resources.Get "lib/shortcode-caniuse/embed.js" -}}
  {{- if hugo.IsProduction | and .Site.Params.cdn -}}
    {{- $source = "https://caniuse.pengzhanbo.cn/embed.js" -}}
  {{- end -}}
  {{- dict "Source" $source "Fingerprint" $fingerprint "Attr" `type="module"` "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/script.html" -}}

  {{- /* shortcode-caniuse.min.js */ -}}
  {{- $options := dict "targetPath" "js/shortcode-caniuse.min.js" "minify" hugo.IsProduction -}}
  {{- if not hugo.IsProduction -}}
    {{- $options = dict "sourceMap" "inline" | merge $options -}}
  {{- end -}}
  {{- dict "Source" (resources.Get "js/shortcode-caniuse.js") "Build" $options "Fingerprint" $fingerprint "Defer" true | dict "Scratch" .Scratch "Data" | partial "scratch/script.html" -}}
{{- end -}}

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.9 | NEW

Inject the shortcode-caniuse.html into the custom-assets through the custom block opened by the FixIt theme in the layouts/partials/custom.html file:

1
2
3
{{- define "custom-assets" -}}
  {{- partial "inject/shortcode-caniuse.html" . -}}
{{- end -}}

Use Shortcode

The caniuse shortcode has the following named parameters:

  • feature [required] (first positional parameter) Feature name
  • past [optional] (second positional parameter) Show the past N versions that match the feature, range is 0 - 5, default is 2
  • future [optional] (third positional parameter) Show the future N versions that match the feature, range is 0 - 3, default is 1

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

Here is an example of usage:

1
2
3
{{< caniuse feature="css-grid" >}}
or
{{< caniuse "css-grid" >}}

The presentation effect is as follows:

1

References

Acknowledgements


Related Content

0%