Hugo のテーマを自作した

Date: 2022/09/18
Tags: Hugo

このブログに使用している Hugo のテーマを変更した。勉強も兼ねて自分でテーマを作成してみた。

作成したテーマ

以下のテーマを作成し、このブログ(m1yam0t0.com)に反映している。

m1yam0t0/hugo-blog-theme

いろいろなページを参考にしながら作成したため、テーマ用のリポジトリを別途用意したが、 個人用で作成したテーマであるため、このブログを管理しているリポジトリで直接管理したほうが良かったかもしれない。 (このリポジトリを Submodule として追加している)

テーマの雛形を作成

まず、作成したテーマの動作確認を行うために、Hugo 公式で用意されているサンプルサイトを clone する。

gohugoio/hugoBasicExample: Example site to use with Hugo & Hugo Themes

git clone https://github.com/gohugoio/hugoBasicExample.git
cd hugoBasicExample

hugo new theme コマンドでテーマを作成するために必要なファイルを生成する。

hugo new theme ${作成したいテーマ名}

コマンドを実行すると themes ディレクトリ配下に以下のようなファイルが生成される。 new-theme で作成した場合、themes/new-theme ディレクトリにファイルが作成される。

themes
└── new-theme # 作成したテーマ名がディレクトリ名になる
   ├── archetypes
   │  └── default.md
   ├── layouts
   │  ├── 404.html
   │  ├── _default
   │  │  ├── baseof.html
   │  │  ├── list.html
   │  │  └── single.html
   │  ├── index.html
   │  └── partials
   │     ├── footer.html
   │     ├── head.html
   │     └── header.html
   ├── LICENSE
   ├── static
   │  ├── css
   │  └── js
   └── theme.toml

生成されたディレクトリについては以下のようになっている。テーマ作成時は layouts と static(CSS) を修正する機会が多いと思われる。

最低限必要な設定を行う

テーマの雛形を作成したのみでは、何もページが表示されない状態となっているため、必要最低限の設定をする。

Hugo のテンプレートの仕組み

設定するに当たり、Hugo のページがどのように生成されるか仕組みを理解したほうが良い。以下の公式ドキュメントに詳細は記載されているため、そちらを読んでおいたほうが良い。

Base Templates and Blocks | Hugo

詳細については、こちらをクリック

Hugo で生成されるページは、baseof.html をもとに HTML が生成される。何も設定していない状態だと、テーマのディレクトリのlayouts/_default/baseof.htmlに以下の内容が出力される。

hugo では HTML 内に Go のテンプレートを混ぜた記述になっており、hugo で使用できるテンプレートを記述してページの構成を組み立てていく。

<!DOCTYPE html>
<html>
  {{- partial "head.html" . -}}
  <body>
    {{- partial "header.html" . -}}
    <div id="content">{{- block "main" . }}{{- end }}</div>
    {{- partial "footer.html" . -}}
  </body>
</html>

{{ partial "hoge.html" . }} と記載すると、テーマのディレクトリ内の layouts/partial 内にある HTML ファイルの内容が出力される。

  {{- partial "head.html" . -}}

上記の例だと、layouts/partials/head.html の内容を出力するということになる。

テーマを編集する際に、再利用する HTML のパーツがあるのであれば、上記のように partial ディレクトリから呼び出す形にすると良い。

初期設定の場合は、以下の 3 つを呼び出すようになっている。初期状態だと中身は空のため、必要な要素をこれらのファイルに追記していく。

以下のように block を定義すると、各ページ(index.html, list.html など)の HTML 内に block を設定すると、その内容が出力される。

{{ block "main" . }}{{ end }}

例えば、以下の内容の index.html を用意し、Hugo でビルドを行う。

{{ define "main" }} これはテストです {{ end }}

ビルドした結果、index.html は以下のように出力される。 (本当は partial の部分も変換されるが、省略している。)

<!DOCTYPE html>
<html>
  {{- partial "head.html" . -}}
  <body>
    {{- partial "header.html" . -}}
-    <div id="content">{{- block "main" . }}{{- end }}</div>
+    <div id="content">これはテストです</div>
    {{- partial "footer.html" . -}}
  </body>
</html>

block は main 以外にも設定できるので、用途によって block を使用してページごとに出力内容を変更できる。

index.html の削除

ブログ用のテーマであるため、トップページである index.html は不要なので削除する。list.html を編集して記事の一覧を表示させるようにする。

トップページを用意したい場合は、index.html をそのまま編集すればよい。

rm -f layouts/index.html

どのページが読み込まれるかについては、以下の公式ドキュメントが参考になる。

Hugo’s Lookup Order | Hugo

list.html の編集

記事の一覧を表示する list.html の中身が空であるため、以下のように編集する。

{{ define "main" }} {{- $title := .Title -}} {{- $pages := .Pages -}} {{-
$summary := false -}} {{- if .IsHome -}} {{- $title = "Homepage" -}} {{- $pages
= where site.RegularPages "Type" "in" site.Params.mainSections -}} {{- $summary
= true -}} {{- end -}}

<h1>{{ $title }}</h1>

{{ range $pages }}
<article>
  <h3>
    <time class="metadata" datetime="{{ .Date }}">
      {{- .Date | time.Format (default "2006/01/02" site.Params.DateFormat) -}}
    </time>
    <a href="{{ .Permalink }}">{{ .Title }}</a>
  </h3>
  {{- if $summary -}} {{ .Summary }} {{- end -}}
</article>
{{ end }} {{ end }}

トップページの場合、.IsHometrue となるので、以下のように if 文で処理を分岐させて、トップページとそうでないページで処理を分けることができる。

{{ if .IsHome }}
...
{{ end }}

ページの一覧を range で繰り返し処理をする際、.IsHome は使用できないので、range 内で処理の棲み分けをしたい場合は、.IsHome を使わず、別途変数を設けて処理を行う必要がある。 以下のように、トップページのみサマリーを表示させたい場合に、$summary という変数を別途設定し、判定させると目的の動作となる。

記入例(一部省略)

  {{- $summary := false -}}
  {{- if .IsHome -}}
    {{- $summary = true -}}
  {{- end -}}

  {{ range $pages }}
    {{- if $summary -}}
      {{ .Summary }}
    {{- end -}}
  {{- end -}}

上記の内容で hugo server -t new-theme すると、トップページと投稿一覧で表示が別れるようになっている。

トップページ 投稿一覧

single.html の編集

それぞれの post を表示するために single.html を編集する。以下のような HTML を記載する。

{{ define "main" }}
<h1>{{ .Title }}</h1>
{{- if not .Date.IsZero }}
<div>
  Date: {{ .Date | time.Format (default "2006/01/02" site.Params.DateFormat) }}
</div>
{{- end }} {{- if .Params.tags }}
<div>
  Tags: {{- range ($.GetTerms "tags") }}
  <a href="{{ .Permalink}}">{{ .LinkTitle }}</a>
  {{- end }}
</div>
{{- end }} {{ .Content }} {{ end }}

上記の内容で、以下の画像のように各 post が表示される。

Post単位のページ

テーマの編集

あとは自分の好みに合わせてテーマを編集していく。最低限必要そうな以下の作業を実施した。

Google Analytics の対応

公式で template が用意されているので、layouts/partials/head.html に以下の記述を追記する。

手元で確認したりするときには挿入したくないので、hugo.IsProduction の判定処理を入れる。
そうすることで、hugo server を実行した場合や、hugo -e development でビルドした場合は挿入されなくなる。

{{ if hugo.IsProduction }}
  {{ template "_internal/google_analytics.html" . }}
{{ end }}

CSS の編集

デザインを自分好みに編集する。static/css に CSS ファイルを追加し、自分好みに編集する。layouts/partials/head.html に以下の記述を追加して、CSS を読み込ませた。 (普段 CSS をいじらないので、めちゃくちゃ苦労した。)

<link rel="stylesheet" href="/css/style.css" />

コードブロックの色を変更するために highlight.js を利用する

Hugo には 標準でコードブロックの色を変更できるようになっているが、用意されている色で自分好みのものがなかったため、highlight.js を使用した。

highlight.js demo で好きな色を選び、以下の内容を head.html に追加すれば良い。

nord.min.css の部分は自分の好きなテーマのものにすれば変更できる。

<link
  rel="stylesheet"
  href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/nord.min.css"
/>
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
<script>
  hljs.highlightAll();
</script>

これだけだと、Code のようなケースのハイライトができないため、以下のようにテーマに合わせた style の設定を追加した。

:root {
  --hljs-background-color: #2e3440;
  --hljs-color: #d8dee9;
}

code {
  background-color: var(--hljs-background-color);
  color: var(--hljs-color);
}

他にもいろいろとやるべきところはあるだろうが、無限に時間が溶けそうなので、ひとまずここまでにする。 気づいたときに少しづつ直せたらと思うが、おそらく当分このままになりそう。

さいごに

普段仕事で、フロントエンドを触ることがないので、HTML や CSS をいじることがなかったため、非常に苦労した。 有名なテーマのソースコードを参照したり、同様の内容の記事を参考にさせていただいた。自分一人の力ではきっとできなかったと思う。

本当にありがとうございました。 この記事が誰かの参考になれば幸いです。

参考