つぶやき Web拍手を送る

ページを増やしてみよう

もう1ページ作るぞ

さっそくページを増やしてみましょう。とりあえず先ほどまで触っていたindex.htmlを同じ階層にコピーして、名前をtest.htmlと変えておきます。 新たにtest.htmlを作成

これまでのindex.htmlと見分けがつくように中身も適当に変更します。

test.html
---
title: テスト
---

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="src/style.css">
</head>
<body>
    <h1>{{ title }}</h1>
    <p>記念すべき2つ目のページ</p>
</body>
</html>

さらに、index.htmlの方からtest.htmlにアクセスできるようリンクを貼っておきます。

index.html
 <!-- 前略 -->
 <body>
     <h1>{{ title }}</h1>
     <img src="src/header.jpg">
     <p>11tyでサイトを作ったぞ~い<br>
+    <a href="test.html">test.htmlへのリンク</a></p>
 </body>
 <!-- 後略 -->

さて、保存して生成されたページたちがどうなったか確認しましょう。 index.htmlの方にはちゃんと今のリンクが追加されていますね。

test.htmlへのリンクが追加されたindex.html

そしてこのリンクをクリックするとtest.htmlが……

「Cannot GET /test.html」...?
???

表示されませんでした。

どうしてこうなったのか、例によって_siteフォルダの中身を確認してみましょう。最初に作ったindex.htmlと同様、作業用フォルダ直下の階層にtest.htmlというファイルを作ったのだから、生成されたページも_site直下にある、と思いきや……

「test」という謎のフォルダが生成されている

_site直下にそれらしきhtmlファイルはなく、その代わりtestというフォルダと、その中にindex.htmlというファイルが作られています。このtestフォルダ内のindex.htmlの内容を見てみると……

2024-07-14_225327

どうやらこれが先ほど増やしたtest.htmlから生成されたページのようです。 ということは、今のアドレスhttp://localhost:8080/test.htmlから拡張子の.htmlを削除し、代わりに末尾に/を付けてhttp://localhost:8080/test/にアクセスしてみると…… http://localhost:8080/test/にアクセスしたところ

今度はちゃんと表示できました!index.htmlのリンクの方もhref="test/" とパスを書き換えて、ちゃんとアクセスできることを確認しておきましょう。

さて、これも11tyの初期設定による挙動で、デフォルトではindex以外の名前のファイル(例:test.html, 001.html, main.htmlなどなんでも)は、_siteフォルダ内でそのファイルが存在するであろう階層にファイル名と同じフォルダが作られ、そのフォルダの中にindex.htmlという名前でページが生成されるようになっています。

なんでこういう挙動なの?

この挙動は Cool URIs don’t changeという考えに基づいたもので、11tyの公式ドキュメントでも言及されています。簡単に言うと、例えば今回の/test/index.htmlのファイル形式が変わって、/test/index.cgi/test/index.phpのようになったとしても、同じアドレス/test/でアクセスすることができる(このように書くと該当フォルダ直下のindexという名前のファイルを(拡張子関係なく)探してアクセスするから)……つまりURLが変わらなくてリンク切れとかしなくていいね、ということらしいです。

この挙動、新しくサイトを構築する場合はリンクの書き方を工夫したり11tyのプラグインを使ったりといった手段で対応できますが、元々別の手段で構築していたサイトを11tyに移植したい(まさにサイトを改装した時の筆者のような)場合には都合が悪いです。できればフォルダの階層を挟まずに、元のファイル名と同じ名前・同じ階層で出力された方が元のファイルを使いまわしやすいでしょう。もちろん、そのようにファイルを出力するための方法も11tyには用意されています。

まずはそれぞれの素材ファイルに個別に設定していく方法を説明します。先ほど作成したtest.htmlのフロントマターの中に、下記のようなpermalinkから始まる行を追加します。この時、右側の値をダブルクォーテーションで囲む必要があるので気を付けてください。

test.html
 ---
 title: テスト
+permalink: "{{ page.filePathStem }}.html"
 ---
 ## 以下略

この状態で上書き保存すると……

test.htmlが_siteフォルダ直下に生成された

元のファイルと同様の階層構造とファイル名(test.html)で出力されました!もちろん、この状態でhttp://localhost:8080/test.htmlにアクセスすれば、今生成されたページが表示されます。 test.htmlをブラウザで表示

ただし、上の画像を見てもらうと分かるように、最初に生成された/test/index.html は削除されずにそのまま残ります。11tyはページの生成は行ってくれますが、不要なファイルの削除は行ってくれません。(もしかしたらこの辺りのことをやってくれる設定や方法があるのかもしれませんが、見つけられませんでした……)
なので、もしも不要なファイルが邪魔だと思ったら手動で削除しておきましょう。もしくは、サイトが完成して公開できる状態になった段階で_siteフォルダ内のファイルを一度すべて削除し、npx @11ty/eleventyコマンドを実行して必要なファイルのみを改めて生成する、という手もあります。

さて、上記の方法ではファイルのフロントマターに記述して設定しましたが、ページが多いといちいち同じ記述をするのは大変そうです。複数のファイルに同じ設定を適用する方法はいくつかあり、この後の説明でも触れる予定ですが、ここでは11tyの設定ファイル.eleventy.jsに記述することで全てのファイルに対して一挙に同じ設定をしてしまおうと思います。.eleventy.js に下記の通り行を追加、上書き保存します。

.eleventy.js
 module.exports = function (eleventyConfig) {
+    eleventyConfig.addGlobalData("permalink", () => {
+        return (data) => `${data.page.filePathStem}.${data.page.outputFileExtension}`;
+    });
     eleventyConfig.addPassthroughCopy("src");
 };

ここでローカルサーバーが起動していれば一度終了させて、_siteフォルダ内に/test/index.htmlが残っていたら削除しておきましょう。test.htmlのフロントマターからpermalink: "{{ page.filePathStem }}.html"の行を削除→保存した上でサーバーを再起動、ページを生成してみてください。/test/index.html が生成されることはなく、test.htmlに直接設定した時と同じ結果になったかと思います。

この設定は何だったの?

---
permalink: "{{ page.filePathStem }}.html"
---

11tyではフロントマターを使用することでファイルに属性とその値を設定することができる、ということを説明しました。そして、11tyではあらかじめ特定の用途に使用するために用意された属性がいくつか存在しています。今回設定したpermalink という属性もその一つで、出力するファイルの場所を _siteフォルダ直下から見たときの出力ファイルのパス」を表す文字列 で設定することができます。
また、値に設定した方の "{{ page.filePathStem }}.html"ですが、{{ page.filePathStem }}と書くことで作業用フォルダ直下(正確にはちょっと違うのですが便宜上こう書きます)から見た時の、ファイルが存在する場所のパスを文字列で得ることができます。ただし、この書き方では拡張子までは取得されない……今回のtest.htmlの例だとtestとなるので、拡張子.htmlは自分で付け足してあげる必要があります。
従って、この書き方によって 作業用フォルダ内での階層の位置を_siteに出力する時のパスとして設定した ため、階層構造を保ったまま出力できたというわけです。
ちなみに、.eleventy.js で記述した設定も、今説明したのと同じことを全ての素材ファイルに適用するよ~ということをYAMLではなくJavaScriptで述べている というのが正体です。
この辺りは公式ドキュメントのこことかここにちゃんとした説明が載っているので、気になる方は覗いてみてください。


これでサイト内にページを増やすのも大丈夫そうですね。今後の解説では上記の出力パスに関する設定はそのまま、作業用フォルダ内の素材ファイルの階層と名前が保たれる状態で進めていくことにします。

レイアウト(Layouts)ファイルを使ってみよう

「静的サイトジェネレーターって?」の項目で、できることの1つとして「複数ページで共通して使うパーツを独立したファイルに切り出して管理できる」というようなことを言いました。ここではそのための機能、「レイアウト」 ファイルについて見ていきます。

まず、11tyにおける 「レイアウト(Layouts)」 ファイルの定義について、公式ドキュメントでは次のように述べられています。

Eleventy Layouts are special templates that can be used to wrap other content.

「11tyにおけるレイアウト(ファイル)とは、他のコンテンツを包むのに使われるような特別なテンプレート(ファイル)のことを指します。」(適当な翻訳)
だそうです。文章だけだといまいちわかりにくいので、実際にファイルを触りながらどんなものか見てみましょう。

まず、ここまでで作成した2つのページ……index.htmltest.htmlについて、改めて中身を確認しましょう。

index.html
---
title: サンプルページ
---

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="src/style.css">
</head>
<body>
    <h1>{{ title }}</h1>
    <img src="src/header.jpg">
    <p>11tyでサイトを作ったぞ~い<br>
    <a href="test.html">test.htmlへのリンク</a></p>
</body>
</html>
test.html
---
title: テスト
---

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="src/style.css">
</head>
<body>
    <h1>{{ title }}</h1>
    <p>記念すべき2つ目のページ</p>
</body>
</html>

この2つを見比べてみると、フロントマターと<body></body> の中身以外は全く同じです。さらに言えば、<body>の中身も<h1>タグの部分は<h1>{{ title }}</h1>となっているため全く同じですね。という訳で、2つのファイルで全く同じになっている部分を別のファイルに分離して、独立して管理できるようにしてみましょう。

まずは分離先のファイルを作っておきましょう。分離先のファイルは_includesという名前のフォルダに入れておく必要があるので、作業用フォルダ直下に_includes フォルダを作り、その中にlayout.htmlというファイルを新規作成します。 _includesフォルダとlayout.htmlを作成

layout.html が作成できたら、このファイルの中に切り出したい部分を記述していきます。今回は<body></body> の外側+<h1>タグを切り出すので、とりあえずこんな感じに書いてみます。

_includes/layout.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="src/style.css">
</head>

<body>
    <h1>{{ title }}</h1>
    <!-- この部分がindex.htmlとtest.htmlで異なる -->
</body>
</html>

index.html(またはtest.html)のhtml部分をコピペして、ファイルごとに異なる部分だけを削除しました。次に、今削除した「ファイルごとに異なる部分」に、{{ content }}と追記して保存します。

_includes/layout.html
 <!DOCTYPE html>
 <html lang="ja">
 
 <head>
     <meta charset="UTF-8">
     <title>{{ title }}</title>
     <link rel="stylesheet" href="src/style.css">
 </head>
 
 <body>
     <h1>{{ title }}</h1>
+    {{ content }}<!-- この部分がindex.htmlとtest.htmlで異なる -->
 </body>
 </html>

これで共通部分のlayout.htmlはひとまずOKです。index.htmltest.htmlの方に戻りましょう。 まず、今作成したレイアウトlayout.html を使用するよ~ということを示してやる必要があります。フロントマターに下記のように追記しましょう。

 ---
 title: サンプルページ
+layout: layout.html
 ---

ここで、属性layoutは11tyがあらかじめ用意してくれている特別な属性のひとつで、「この素材ファイルではこのレイアウトファイルを使用するよ~」というのを示すために使われます。また、11tyは初期設定で_includes フォルダ内にレイアウトファイルがあるものと判断し、この中から属性layoutに設定したファイルを探してくれます。そのため、ここでの指定は _includes フォルダ直下からの相対パス で書けばOKです。

そして、index.htmltest.htmlの中身から、先ほどlayout.htmlに切り出した共通部分を削除してしまいましょう。

index.html
---
title: サンプルページ
layout: layout.html
---
    <img src="src/header.jpg">
    <p>11tyでサイトを作ったぞ~い<br>
    <a href="test.html">test.htmlへのリンク</a></p>
test.html
---
title: テスト
layout: layout.html
---
    <p>記念すべき2つ目のページ</p>

ずいぶん記述量が減ってすっきりしました。それではこれを上書き保存して、生成されるページがどうなるのか、確認しましょう。

index.html

test.html

……どうでしょうか?レイアウトファイルを使用する前と同じ表示になったでしょうか?
ここまでうまくできたなら、別ファイルに切り出したことによる恩恵を感じてみましょう。先ほど作ったlayout.htmlを少し書き換えてみます。

_includes/layout.html
 <!DOCTYPE html>
 <html lang="ja">
 
 <head>
     <meta charset="UTF-8">
+    <title>{{ title }} - Sample Site</title>
     <link rel="stylesheet" href="src/style.css">
 </head>
 
 <body>
     <h1>{{ title }}</h1>
+    <hr>
     {{ content }}<!-- この部分がindex.htmlとtest.htmlで異なる -->
 </body>
 </html>

<title>タグ内にサイト名っぽいものを追加したり、<h1>タグの下に罫線を引いたりしてみました。これを保存すると…… layout.htmlの変更が反映されたindex.html layout.htmlの変更が反映されたtest.html

index.htmltest.html両方に今の変更が反映されました!


ここまで実際に触ってみてレイアウトファイルの挙動についてなんとなくお分かりいただけたでしょうか。 ここで、最初に引用した11ty公式のレイアウトの説明について、もう一度見てみます。

Eleventy Layouts are special templates that can be used to wrap other content.

(11tyにおけるレイアウト(ファイル)とは、他のコンテンツを包むのに使われるような特別なテンプレート(ファイル)のことを指します。)

レイアウトの前に、テンプレート が何なのかという話ですよね。11tyにおいて 「テンプレート(templates)」 とは、 サイトを構成するhtmlを出力するための素材となるファイル のことを指します。これまでの説明で「ページの素材となるファイル」「素材ファイル」と呼んでいたもののことです。
そして、「レイアウト」は他のコンテンツを包む……言い換えれば 「外枠」 となるような「素材」である、と言えます。

改めて文章で説明してみると、テンプレートファイル(素材ファイル)から「このレイアウトファイルを使います」と呼び出すと、呼び出されたレイアウトファイル内の{{ content }}の部分に呼び出し元であるテンプレートファイルの内容がはめ込まれ、この状態を基にページを生成する……という動きをします。

レイアウトファイルの挙動の図解。テンプレートがレイアウトに「包まれて」出力される様子

また、レイアウトファイルも「ページを生成する素材となるファイル」ではあるので、公式の説明ではレイアウトファイルはテンプレートファイルの一種である、という書き方がされていたんですね~。なるほど~。


蛇足:単語の意味について

趣味・同人系の個人サイト制作に馴染みのある方は、ページのデザイン・枠組みがあらかじめ用意されており、そこに自分のコンテンツをはめ込めばいい感じのサイトができる、『htmlテンプレート』の配布サイトを見たことがあったり、あるいは使ったことがあるかと思います。ここで説明した11tyにおける「レイアウト」は、このテンプレート配布サイトの『テンプレート』に似たようなものと考えていいでしょう。
ただ、11tyではページ生成の素となるファイル全般を指して「テンプレート」と呼んでいる……つまりはめ込むコンテンツの方も「テンプレート」、枠組みの方も「レイアウト」という名の「テンプレート」なので、上記の『テンプレート』に馴染みのある方だと公式ドキュメントを読むときなど混乱してしまうかもしれません。かくいう筆者も混乱してました。


ここで補足……今作成したlayout.html<head>タグ内、スタイルシートを呼び出す部分についてですが、今はindex.htmlあるいはtest.htmlからの相対パスになっているかと思います。

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

index.htmltest.htmlも同じ階層(ルート直下)にあったので、これでも特に問題は起こりませんでした。しかし今後ページを増やしていく時のことを考えると、さすがに全てのページを作業フォルダ直下に置いておくのは現実的ではないでしょう。様々な階層のファイルから同じレイアウトを呼び出すことになると思いますが、そうなると上のような相対パスの書き方では困ってしまいます。そのため、様々な場所から呼び出されることになるレイアウト、あるいは次に説明する共通パーツのファイル内では 「ルートパス」 ……/から始まる、サイトの一番上の階層を基準としたパスで記述するのがおすすめです。例えば上記のスタイルシート呼び出しは、

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

このように書くとよいでしょう。

共通パーツを切り出してみよう

前の項目ではコンテンツをはめ込む枠のような「レイアウト」について触れてきました。次は「レイアウト」とはまた違った、共通部分を切り出す方法について解説します。

例えば、先ほど作ったlayout.htmlにヘッダーとフッターを追加することを考えます。直接layout.htmlにヘッダーとフッターをコーディングしてしまってもいいのですが、折角なのでそれぞれ独立したパーツとして別のファイルに分離してみましょう。イメージとしてはこんな感じです。

ページを構成するパーツを別のファイルに分けてみよう の図

まずはパーツの中身を記述をするファイル……図で言うところのheader.htmlfooter.htmlを作ります。場所はlayout.htmlを配置してあるのと同じ_includesの中に、パーツであることが分かりやすいようにpartialsという名前のフォルダを作って置くことにします。今回説明するパーツや前項のレイアウトなど、他のファイルから呼び出して使うファイル(テンプレート)は全て_includesという名前のフォルダの配下に置くようにしましょう。

パーツとなるファイルをpartials内に作成

ファイルが出来たら中身を書いていきましょう。とりあえず超簡単な中身で試してみます。

_includes/partials/header.html
<header>
    <p>ヘッダーだよ</p>
</header>
_includes/partials/footer.html
<footer>
    <p>フッターだよ</p>
</footer>

そしたら、layout.htmlからこれらを呼び出す記述をしましょう。パーツを呼び出したい場所に {% include "呼び出したいパーツのパス" %}と書きます。この時、呼び出したいパーツのパスはレイアウトの時と同様、_includes直下からの相対パスでOKです。今回の場合だと以下のような感じになります。

_includes/layout.html
 <!DOCTYPE html>
 <html lang="ja">
 
 <head>
     <meta charset="UTF-8">
     <title>{{ title }} - Sample Site</title>
     <link rel="stylesheet" href="src/style.css">
 </head>
 
 <body>
+    {% include "partials/header.html" %} <!-- ←ヘッダーを呼び出している -->
     <h1>{{ title }}</h1>
     <hr>
     {{ content }}
+    {% include "partials/footer.html" %}<!-- ←フッターを呼び出している -->
 </body>
 </html>

それではこの状態で保存して、生成されるページがどうなるか確認してみましょう。

ヘッダー・フッターが挿入されたindex.html

ヘッダー・フッターが挿入されたtest.html

index.htmltest.htmlはどちらもlayout.htmlを使っているので、両方のページに追加したヘッダーとフッターが挿入されていますね。もちろん、フロントマターで設定した属性をパーツ内で使用することもできます。試しにヘッダーに属性title の値を表示させてみましょう。

_includes/partials/header.html
 <header>
+    <p>ヘッダーだよ {{ title }}</p>
 </header>

ヘッダー内に属性titleの値が表示された ちゃんと反映されましたね。

また、今はレイアウトファイルlayout.htmlからパーツを呼び出しましたが、レイアウトではない、普通のテンプレートファイル内でもパーツの呼び出しは可能です。試しにtest.htmlで今作ったパーツを呼び出してみましょう。

test.html
 ---
 title: テスト
 layout: layout.html
 ---
 
     <p>記念すべき2つ目のページ</p>
+    {% include "partials/header.html" %}
+    {% include "partials/footer.html" %}
+    {% include "partials/header.html" %}
+    {% include "partials/footer.html" %}

結果がこちら。一番上と一番下のヘッダー・フッターはlayout.htmlから呼び出されているもの、その他がtest.htmlで呼び出したものです。ちゃんと記述通りに呼び出されていますね。 パーツをいっぱい呼び出したtest.html

この機能を活用すれば、今の例のようなヘッダー・フッターはもちろん、工夫次第で前後ページへのナビゲーションリンクなどもパーツとして分離し、使いまわすことが可能になります。


ここまでの内容で11ty……というか静的サイトジェネレーターの便利なところ、感じてもらえたでしょうか。

次の項目からは、実際にサイトを構築する時の一例として、イラスト展示を目的としたサイトを作ってみながら11tyでできることを引き続き見ていきたいと思います。