Hugoは静的サイトジェネレータの中でもだいぶ柔軟性のあるツールだと思う。
でも、その分気をつけなきゃいけないことがあったり、実現する方法が分かりづらかったりすることが多いので、 そういう点をまとめてみる。
まずはテーマを作る。 この章では、テーマを作るための環境の作り方を解説する。
HugoにはHugoBasicExampleという、サンプルの記事や基本的な機能を使ったテスト用レポジトリが用意されている。
spf13/HugoBasicExample: Example site to use with Hugo & Hugo Themes
Hugoのテーマづくりは、このレポジトリのcloneから始まる。
次にテーマの雛形を作る。 Hugoにはテーマの雛形を作り上げるコマンドがある。
hugo new theme [name]
hugo new
コマンドは、新しくサイトを作るときやページを作るときにも使われる。
とにかく、何か新しいものを作るときに使われるコマンドで、テーマを作るときもこれを使う。
cloneしたHugoBasicExampleのルートディレクトリでコマンドを実行すれば、./themes/XXXX
に雛形が作られる。
後述するthemes.gohugo.ioに載せてもらうために必要なファイルも揃っているので、0からファイルを作るよりこのコマンドを使うのが賢明だと思う。
Hugoのページ構造はだいたい次の通り。
で、hugo new
で作られるテーマファイルの構成は次の通り。
.
├── LICENSE.md
├── archetypes
│ └── default.md
├── layouts
│ ├── 404.html
│ ├── _default
│ │ ├── list.html
│ │ └── single.html
│ ├── index.html
│ └── partials
│ ├── footer.html
│ └── header.html
├── static
│ ├── css
│ └── js
└── theme.toml
実は、この雛形には足りないものや要らないものもある。 それはおいおい自分の好きなように足したり消したりすればいい。
テンプレートファイルと対象ページの紐付けは、実は少しややこしい。
なぜかというと、Hugoは対象のテンプレートを特定の順番で走査する仕組みになっていて、 ページごとに細かくデザインを変えたり、全体で1つのテンプレートに統一したりと、融通が効くようになっている。
listページを例に具体的な構造を示すとこうなる。
/layouts/section/SECTION.html
/layouts/_default/section.html
/layouts/_default/list.html
/themes/THEME/layouts/section/SECTION.html
/themes/THEME/layouts/_default/section.html
/themes/THEME/layouts/_default/list.html
上から順に、最初に見つかったテンプレートを採用する。
つまり、最終的に/themes/THEME/layouts/_default/list.html
まですべて見つからなければ、何も描画されない。
Hugoの最も便利なしくみの1つとして、テンプレートファイルは./themes
とルート直下のものでそれぞれ用意できるような仕様がある。
つまり、themes
においたテーマファイルをユーザが独自にオーバーライドできるようになっている。
先の例にあげたlistで言えば、/layouts
と/themes
で同じファイルを走査しているのがわかる。
/layouts/section/SECTION.html
/layouts/_default/section.html
/layouts/_default/list.html
/themes/THEME/layouts/section/SECTION.html
/themes/THEME/layouts/_default/section.html
/themes/THEME/layouts/_default/list.html
例えば、ユーザがあなたのテーマを使ってくれたとする。
そのまま使えばあなたがデザインしたとおりに描画されるが、対象のテンプレートファイルと同じ名前をルートディレクトリに配置することで、 そのファイルだけを独自に変更することができる。
ここで大事なのが、これはテンプレートファイルに限った話ではなく、CSSや画像などの静的ファイルにも同じことが言えるということ。
これをうまく利用することで、ユーザが豊富なバリエーションで使うことができるテーマを作ることができる。
ここまでで、Hugoのテーマづくりにおける基礎的な解説はできたと思う。
しかし、実際に手をつけるとなるとどこから着手していいかわからなくなると思う。 というわけで、僕がテーマを作るときの作業順序をまとめてみる。
Hugoは./layouts/index.html
がなければ、./layouts/_default/list.html
が採用される。
僕は複雑な構造にするのが嫌なので、まず./layouts/index.html
を消してしまう。
トップとそれ以外の記事一覧で表示を変えたい場合は、if文と.IsHome
でなんとかする。
トップと一覧が大幅に乖離する場合は、ちゃんと分けたほうがいい。
後述するblock templatesを使うため、baseof.html
を作る。
ここでHTMLに必要な<!DOCTYPE html>
やmeta
タグの配置、ヘッダとフッタのマークアップとデザインをする。
次にlist.html
を作る。
前述したとおり、Hugoは./layouts/index.html
がなければ、./layouts/_default/list.html
が採用される。
このタイミングで、トップページのマークアップとデザインを終わらせる。
Hugoは断片的なパーツ、例えば一覧表示用の記事など、使い回しをする素材を切り出すことを推奨している。
これはPage
が持つ.Render
を使って実現する。
Hugo - Hugo Template Functions
./layouts/_default/list.html
には最新記事の一覧を表示する場合がほとんどであるため、
./layouts/_default/list.html
で作った記事1つ1つをli.html
に切り出す。
次に記事詳細を作ってしまう。 これはここまでで説明したものとなんらかわりないので、特筆なし。
次にtermsのページを作る。 このページは質素になりがちなので、デザインが難しい。
わすれがちなのが404.html
。
ちゃんと作っておかないと空白のページが作られる。
ここまでで基本的なテーマは作れるまでの説明をした。 しかし、これだけだときっとハマるであろう落とし穴がいくつかある。
画像や静的資材の参照には相対パスを使ってはいけない。これはHTML上にあらわれるあらゆるファイルすべてに言える。
ユーザはドメインのルートであなたのテーマを使うとは限らない。 例えばthemes.gohugo.ioがいい例。
各テーマのデモはサブディレクトリで構成される。 つまり、相対パスを使ってしまうと参照が壊れてデザインが崩れる。
{{ .Site.BaseURL}}
を使って絶対参照で構築するべき。
よくあるのが、Taxonomyに使われる値をべた書きしてしまうものだ。
categories
やtags
など。
これをマジックナンバーで書いてしまうと、ユーザが独自にTaxonomyを定義できない。
Hugoは.Site.Taxonomies
で定義されたTaxonomyをすべて取得できるので、できるだけ柔軟に使えるようにしよう。
Hugo側でcategories
とtags
はデファクトみたいなスタンスを取っているので、それを理解した上でべた書きするのはありだと思う。
サイトを作る上で、誰もが使いたいであろう機能がある。 そういった機能はできるだけつけたほうがいい。
代表的なものがGoogleAnalytics。
Hugoはconfig.toml
にGoogleAnalyticsのUserAgentを記述できるようになっている。
googleAnalytics = "UA-123-45"
この値が設定されていればGoogleAnalyticsのタグを配置するべきだし、 設定されてないなら出すべきじゃない。
そういったときにはwith
が便利にはたらく。
Conditionals | Hugo - Go Template Primer
GoogleAnalyticsのタグは_internal/google_analytics.html
という内部テンプレートが用意されている。
自分で書くよりもこれを呼び出してしまえば、値の設定、未設定の判定もやってくれる。
静的サイトのコメント機能でもっとも有名なDisqus。 これもGoogleAnalyticsと同様、変数名が決まっている。
disqusShortname = "XYW"
DisqusもGoogleAnalytics同様、内部テンプレートが用意されているので、それを呼び出すのがいい。
HugoはHugoのコミュニティを成長させるために、metaタグにgeneratorを明記することを推奨している。
1<meta name="generator" content="Hugo 0.17-DEV" />
generatorは{{ .Hugo.Generator }}
でタグとともに出力される。
ユーザに影響をあたえるものではないが、Hugoの発展のためにできるかぎり入れてあげたい。
generator-meta-tag | Hugo - Creating a Theme
シェアボタンも、基本的にはどのサイトにも必要になる。 これは国によって使われるサービスやSNSが異なるため、可変にできるとなおいい。
また、シェア自体いらないという場合もあるため、config.tomlで表示、非表示が切り替えられたり、
記事単位に.Params
で切り替えられたりするといいと思う。
ここまでで、必要最低限なものが揃ったテーマを作るための説明はできたと思う。 次は少し発展させて、ユーザが柔軟にテーマを使うための機能を作る。
だいたいのWebサイトには、メニューが配置される。 HugoにはMenusという機能があり、これを使うこともできる。
が、実際にこれを使ってMenuを形成している人は少なそうな印象。 そのため、config.tomlなどで設定できるようにしているテーマが多く見られる。
自分がもっとも適切と思える形で実装し、READMEで設定の仕方を説明するといい。
Wordpressやその他のブログシステムにはShortcodesというとてつもなく便利な機能がある。
もちろんHugoにもあるので、基本的なものはテーマ側で用意しておくといい。
これはマークダウン内で定型的なHTMLを差し込むことができるもの。 例えば、画像やiframe、Twitterの埋め込みなどは、毎回HTMLを書くよりも手軽に呼び出せるようにしたかったりする。
テーマが用意するShortcodesとしては、画像の埋め込みと右寄せ、左寄せくらいを提供できるといい。 機能を作ったら、README.mdに使い方を書いておく。
ユーザが独自に、デザイン的なオーバーライドをしたい場合がある。
styles.css
1つ用意しておけば、ユーザがまるまるHugoのルートディレクトリにコピーしてオーバーライドすることもできるが、
全体をコピーしないといけなくなる。
そういったときのため、custom.cssという空っぽのスタイルシートを用意して読み込ませておくといい。 ユーザは必要な場合のみcustom.cssを作ればいいし、そうでないなら空っぽのまま使われるだけ。
テーマのバージョンアップをしたときも、影響を少なくすることができる。
Hugoは全世界のあらゆる国で使われている。 テーマもできるだけ、特定の国に依存しないようにする。
まず言えるのが、日付の形式だ。
日本は2016/01/01
といった形式だが、世界では違う。
これはできるだけ、柔軟に変えられるといい。
例えば、config.tomlに次のように設定できるようにする。
[params]
dateformat = "Jan 2, 2006" # Optional
テンプレートファイルでは次のように描画する。
{{ .Lastmod.Format ( .Site.Params.dateformat | default "Jan 2, 2006") }}
英語圏ではHelveticaやArialなどでいいフォントも、国によってはそうとも言えない。
これもDateFormatと同じように、config.tomlなどで設定できるといい。 custom.cssを用意する方法でも良いが、フォントだけのためにファイルを作らせるよりは、設定で変えられると親切だと思う。
ここでは、テーマを作る上でのさらに発展的なノウハウについてまとめる。
すでに登場したが、Hugoはblock templatesという機能が使える。
これはベースとなるテンプレートと子となるテンプレートを用意できる機能で、 HTMLの共通的な部分をベーステンプレートに切り出すことができる。
しかし、このHugoのこの機能には現在バグがあり、時折Golangのエラーメッセージを吐いてクラッシュする。
fatal error: concurrent map read and map write
この記事を書いている今も、かなりの回数クラッシュしている。
おそらく記事生成をgoroutineで回していて、その中でmapへの不適切なアクセスが走っているのだと思う。 そのうち直ると思っているが、気になる人はblock templates自体を使わないほうがいい。
Concurrent map read and write error in template handling · Issue #2224 · spf13/hugo
Issueもあがっているが、再現できていないらしい。
AMP対応は制約がいくつか付くだけなので、そこまで難しい実装ではない。
おそらくいちばんこまるのがスタイルシートの導入だと思う。
僕は./layouts/partials/styles.css
にスタイルを配置して、次のようにしてインラインCSSとして描画している。
<style amp-custom>
{{ replaceRE " +" " " (replaceRE "\n" "" (partial "styles.css" .)) | safeCSS }}
</style>
テーマを作ったら、Hugoのテーマポータルであるthemes.gohugo.ioに載せてもらうための手順を進める。
まず、theme.tomlを書く必要がある。
これはhugo new
で雛形が作られているので、それにそって書けばいい。
基本的な説明は、Hugoのテーマ用レポジトリのREADMEにある。
spf13/hugoThemes: All Themes Hugo
themes.gohugo.ioに表示されるサムネイルが2つの異なるサイズで必要になる。
Thumbnail should be 900×600 in pixels
Screenshot should be 1500×1000 in pixels
Media must be located in:
[ThemeDir]/images/screenshot.png
[ThemeDir]/images/tn.png
一番映えるページでスクリーンショットを撮り、配置する。
README.mdはユーザにもっとも近いドキュメントになる。 ここにはできるかぎり親切な情報を載せたほうがいい。
例えば次のようなもの。
必要であれば、画像も使って説明をする。
ここまでできたら、自分のテーマレポジトリをHugoのテーマ用レポジトリにIssueとして連絡すればいい。
spf13/hugoThemes: All Themes Hugo
コントリビューターの方が確認して、修正すべきポイントがあればIssueをあげてくれるし、 問題なければ取り込んでCloseしてくれる。