Hugo のレイアウトの仕組み
テーマの無いテスト用サイトを作る
普通はthemes
DIRに任意のテーマをインストールすることで、サイトの基礎になる最低限の見た目が表示されるのだが、Hugo のレイアウトの仕組みを理解するためにわざと既存のテーマをインストールせずに進めてみる。
新しい Hugo のテスト用サイトを作りサーバーを起ち上げる.
$ hugo new site hugo-test-site
$ cd hugo-test-site/
$ hugo server
Hugo は最低限のテンプレートが無いとHTMLを生成しないので、このままでブラウザからhttp://localhost:1313/
にアクセスしても真っ白で何も表示しない.
最低限必要となるテンプレート
Hugoは次の4つの基本テンプレートが存在する事でページを生成することになている。
- layouts/index.html(トップページ)
- layouts/_default/baseof.html(全ページで使われるBase Template)
- layouts/_default/list.html(セクションやTaxonomyごとの記事一覧ページ)
- layouts/_default/single.html(記事個別ページ)
順に手書きで作ってみる.
トップページ
layouts/index.html
を作る.
[layouts/index.html
]
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <link rel="stylesheet" href="{{ .Site.BaseURL }}/style.css"> <title>Hugo テスト</title> </head> <body> <header> <div class="header_title">Hugo テスト</div> </header> <footer> ここがフッター </footer> </body> </html>
一応{{ .Site.BaseURL }}/style.css
でサイト全体のスタイルを定義したCSSを読み込む.これはstatic/style.css
として置かれたファイルで、CSSファイル以外に画像などもstatic/img/001.jpg
などとして配置すると、テンプレート中でimg/001.jpg
でアクセスできる.style.css
には最低限のスタイルのみが定義されている.これからパーツを追加していく過程で適宜スタイルも追加していくが cssの詳細は割愛する.
これだけでヘッダーとフッターに文字が表示された極めて最低限の表示がされるはずだ.これが URLlocalhost:1313/
で表示されるトップページになる.
個別の記事ファイル
しかしまだ本体メインの中身が無い.そこでcontent
以下に記事ファイルを用意する.1つめにcontent/sample1.md
を作る.
[content/sample1.md]
--- title: "Sample1" date: 2020-08-25T18:03:25+09:00 # draft: true --- 最初の記事です. ここに内容を書きます.
Front matter のdraft
項目はコメントアウト又は削除する.
ただ記事ファイルを作っただけではトップページにコンテンツを表示する仕組みが無いので何も変わらない.そこでトップページにはたいてい記事のリストを表示しておくので、layouts/index.html
に記事へのリンクリストを並べたメインコンテンツを表示するブロックを追加する.
[layouts/index.html
]
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <link rel="stylesheet" href="{{ .Site.BaseURL }}/style.css"> <title>Hugo テスト</title> </head> <body> <header> <div class="header_title">Hugo テスト</div> </header> <div class="contents"> <ul> {{ range .Data.Pages }} <li><a href="{{ .RelPermalink }}">{{ .Title }}</a> ({{ .Date.Format "2006-01-02" }}) {{ end }} </ul> </div> <footer> ここがフッター </footer> </body> </html>
.Data.Pages
: すべてのコンテンツの情報が格納された変数。 range から end がループで、その間はループの現在値(ページの値)に . で直接アクセスできる.
.Date.Format "日付表示の形式サンプル"
の形で、各記事の Front matter に書かれたdate
項目の日付をフォーマットして表示する.
記事が一つではリストの実感が無いので、sample2.md
〜sample4.md
といった形でもう2〜3記事追加して試してみたい.ただこのまま記事を追加しても、トップページのコンテンツには記事リストは表示されるが、個別記事のリンクをクリックした際の元になるレイアウトファイルが無いので404エラーになる.
個別記事用のレイアウト
そこで個別記事用のレイアウトlayouts/_default/single.html
を用意する.
[layouts/_default/single.html]
<div class="site-content"> <h1>{{ .Title }}</h1> <time>{{ .Date.Format "2006-01-02" }}</time> <article> {{ .Content }} <!-- ここに本文すべてがはいる --> </article> </div>
これでトップページの記事リンクをクリックすると、個別の記事内容が表示される.
リスト用のレイアウト
トップページのコンテンツ表示部分の.Data.Pages
を.Site.Pages
に変えてみる..Site.Pages
はデフォルトで日時 (.Date) の一番新しいものから昇順に並べられたPage配列を返す.
するとcontent
フォルダ内の記事ファイルしか表示されていなかった記事一覧に、作った憶えのないCategories
とかTags
とかconfig.toml
で指定したサイトタイトルのリンクなどが表示される.これらのリンク先もやはり専用レイアウトがまだ用意されていないのでクリックしても何も表示しない.ただし404エラーではなく、コンソールには「"taxonomy"表示のためのHTMLが無い」といった警告が出ているはずだ.
WARN 2020/08/25 21:08:37 found no layout file for "HTML" for kind "taxonomy": You should create a template file which matches Hugo Layouts Lookup Rules for this combination. Total in 3 ms
そこで/layouts/_default/list.html
というレイアウトファイルを用意する.これはコンテンツリストを表示することを目的とした基本的な共通テンプレートファイルだ.
[/layouts/_default/list.html]
<div> <ul> {{ range .Pages }} <li><a href="{{ .RelPermalink }}">{{ .Title }}</a> ({{ .Date.Format "2006-01-02" }})</li> {{ end }} </ul> </div>
ここで記事のカテゴリーやタグの一覧を表示するために、個別の記事の Front matter にそれらを適当に追加しておく.
--- title: "Sample1" date: 2020-08-25T19:02:56+09:00 # draft: true tags: [ example, test ] categories: [ Hugo ] ---
これでトップページのCategories
とかTags
などのリンクをクリックするとカテゴリーやタグで分類された記事のリストが表示される.このリスト表示にさきほど作った/layouts/_default/list.html
が使われている.
Section の仕組みで記事を分類する
content
フォルダ内にサブフォルダを作ると Section になる.例えばcontent/posts/
という形にサブフォルダを作って、その下に先程の記事ファイルを置くと、"baseURL"/posts/sample1
といったURLでアクセスできて、それらの記事はposts
セクションに分類される.
実際にやってみると、トップページのコンテンツリストには、個別記事やカテゴリーなどのリンクとは別にPosts
というリンクが貼られており、クリックするとcontent/posts/
配下に移動した個別記事へのリンクリストのページが表示される.このリストの表示に使われているレイアウトも、さきほどの/layouts/_default/list.html
だ.
partial でサイトの部品をつくる
サイトを構築する際に、共通部分を部品化する仕組みがたいていCMSにはあるが、Hugo も partial という仕組みを備えている.とりあえずlayouts/index.html
に直書きしているヘッダーやフッターを部品化してみる.
layouts/partials/
というフォルダを作って、配下にheader.html
とfooter.html
を置く.
[layouts/partials/header.html]
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, user-scalable=no"> <link rel="stylesheet" href="{{ .Site.BaseURL }}/style.css"> <title>Hugo テスト</title> </head> <body>
[layouts/partials/footer.html]
<footer> ここがフッター </footer> </body> </html>
layouts/index.html
からこれらを読み込む.ヘッダーとフッターを展開したい位置に partialコマンドでこれらの部品を読み込む.コマンドの最後のピリオドは partial で読み込んだデータをこの位置に展開するためのもので必要.
[layouts/index.html]
{{ partial "header" . }} <header> <div class="header_title">Hugo テスト</div> </header> <div class="contents"> <ul> {{ range .Site.Pages }} <li><a href="{{ .RelPermalink }}">{{ .Title }}</a> ({{ .Date.Format "2006-01-02" }})</li> {{ end }} </ul> </div> {{ partial "footer" . }}
list.html
やsingle.html
でも同様の partialコマンドのコードを書けば共通の部品にはなるが、ここではもっと共通化を進めるためにBase Template の仕組みを使う.
Base Template でサイトの共通部分をまとめる
ヘッダーやフッターの partial をこのままlayouts/index.html
やlayouts/_default/single.html
、/layouts/_default/list.html
といった各レイアウトファイルに埋め込んでいってもそれなりの共通化はできるが、Base Template の仕組みを使えばもっとスマートになる.
各テンプレートの中身を{{ define "main" }}
〜{{ end }}
で囲み、layouts/_default/baseof.html
内で{{ block "main" . }}
〜{{ end }}
で囲んだ中にそれぞれのページの内容が埋め込まれる.ヘッダーやフッターもこのbaseof.html
の中で読み込めばindex.html
、single.html
、list.html
などに partial で書く必要は無い.
ここではより共通化を進めるためにindex.html
にあったサイトヘッダーもlayouts/partials/site-header.html
として部品化し、baseof.html
の中で読み込んだ.これで全ページで共通のヘッダー、サイトヘッダー、フッターが表示される.
ちなみに DOCTYPE宣言とか html や body タグといった全体の構造枠(外枠)はヘッダーやフッターに欠片があるのは気持ちわるいので、baseof.html
にまとめた。この方が全体の関連が把握しやすいと思う。
[layouts/partials/site-header.html]
<header> <div class="header_title">Hugo テスト</div> </header>
[layouts/_default/baseof.html]
<!DOCTYPE html> <html lang="ja"> {{ partial "header" . }} <body> <div id="main"> {{ partial "site-header" . }} <div class="contents"> {{ block "main" . }} <!-- ここに各ページの内容がはいる --> {{ end }} </div> </div> {{ partial "footer" . }} </body> </html>
[layouts/_default/index.html]
{{ define "main" }} <ul> {{ range .Site.Pages }} <li><a href="{{ .RelPermalink }}">{{ .Title }}</a> ({{ .Date.Format "2006-01-02" }})</li> {{ end }} </ul> {{ end }}
[layouts/_default/list.html]
{{ define "main" }} <div> <ul> {{ range .Pages }} <li><a href="{{ .RelPermalink }}">{{ .Title }}</a> ({{ .Date.Format "2006-01-02" }})</li> {{ end }} </ul> </div> {{ end }}
[layouts/_default/single.html]
{{ define "main" }} <div class="site-content"> <h1>{{ .Title }}</h1> <time>{{ .Date.Format "2006-01-02" }}</time> <article> {{ .Content }} <!-- ここに本文すべてがはいる --> </article> </div> {{ end }}