0 から始める Jekyll 超入門 #2  - 独自テーマ作成編

この記事は 0 から始める Jekyll 超入門 の 2番目の記事です。
- [#1 環境構築編](https://qiita.com/YuSan19/items/8371def087e51f450d3a)
- #2 独自テーマ作成編 <-今回

---

# 独自テーマとは
Jwkyll では独自にテーマを作成してオリジナルなデザインのサイトを作ることができます。
\#1 の記事で、記事の作成の時に layout: post としたのを覚えているでしょうか？
これは、テーマの post レイアウトを使用する、という意味です。

Jekyll で独自テーマを作成するのは非常に簡単で、 `_layouts` ディレクトリにレイアウトを作り、それを posts や page でこの前やった通りに指定することで適用する、という感じになります。

ここでは、トップページと投稿ページに独自テーマを適用するようにしていこうと思います。

> 注）なお、あくまで操作やロジックの部分に焦点を当てたいため、デザインの部分はかなり手抜きします。
きれいに作りたい方は自分で1から scss を書いたり、Bootstrap や Bulma などの cssフレームワークを利用したりしてください。

それでは早速作っていこうと思います。

# テーマを作る前の準備
## 不要なファイルを削除したり必要なファイルを作成

まず最初に、
作業ルートディレクトリに `_layouts` ディレクトリを作ってください。
前回からの確認のため、ディレクトリの図を一応貼っておきます。
(自動生成される .gitignore に指定されている物は除外しています。

```
.
├── 404.html
├── Gemfile
├── Gemfile.lock
├── _config.yml
├── _layouts
├── _posts
│   ├── 2020-05-19-hello-world.md
│   └── 2020-05-19-welcome-to-jekyll.markdown
├── about.markdown
└── index.markdown
```

index.markdown と about.markdown はとりあえず不要なので削除しましょう。

## 設定ファイルを編集する

`_config.yml` を開き以下の通りに上書きします。

```_congig.yml
title: 0 から始める Jekyll 超入門  # サイトのタイトル
description: >- # サイトの説明を書く
  サイトの説明を書いてください。
  200文字程度がいいと一般には言われていたりします。

baseurl: "" # ここは無視してOKです
url: "https://<type-your-domain>" # サイトのドメインを入力します。

plugins: # 使用するプラグインがあればここに追加します
  - jekyll-feed

exclude: # サイトの生成に含めないものをここに追加します
  - .sass-cache/
  - .jekyll-cache/
  - gemfiles/
  - Gemfile
  - Gemfile.lock
  - node_modules/
  - vendor/bundle/
  - vendor/cache/
  - vendor/gems/
  - vendor/ruby/
```

前準備などは以上です。

# Jekyll の Lquid 構文
レイアウトを作っていく前に、簡単な構文の解説をします。
まず、 Jekyll は基本的に html をベースに書くことができます。
その上で、 Liquid と呼ばれる構文を使用することもできます。
Liquidは3つの主なパート：オブジェクト、タグ、フィルタを持つテンプレート言語とされています。

## オブジェクト
Liquid では、 `{{` と `}}` で各種オブジェクト（変数など）を括ることで、出力することができます。
例えば、

```html
<title>
 {{ page.title }}
</title>
<!-- page.title = "こんにちは, 世界！" ->
```
このようなソースがある場合、生成される html ファイルは、以下のようになります。

```html
<title>
  "こんにちは, 世界！"
</title>
```

## タグ

Liquid タグを使用することで、ロジックのあるテンプレートを使用することができます。
Liquid タグを使用する場合は、 `{%` と `%}` で囲みます。

例えば以下のような場合、 page.title が `true` の場合、
すなわち値が存在する場合には `{{ page.title }} - {{ site.title }}` が出力される、
すなわち "こんにちは, 世界！ - Jekyll 超入門" が出力されます。
`false` の場合は、 
{{ site.title }} すなわち "Jekyll 超入門" が出力されるわけです。

```html
<title>
  {% if page.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %}
</title>
<!-- page.title = "こんにちは, 世界！", site.title = "Jekyll 超入門" ->
```

他にも Jekyll では様々な Liquid タグが用意されているので、[こちら](http://jekyllrb-ja.github.io/docs/liquid/tags/) を読んでおくといいかもしれません。

## フィルタ

Liquid フィルターは、Liquid オブジェクトの出力を加工することができます。
オブジェクトの出力を `|` で区切ることで実装できます。

例えば、以下のソースの場合を見てみましょう。
ちなみに、 `page.date` は記事の作成日を出力するオブジェクトで、
記事のファイルの命名規則である `yyyy-mm-dd-<title>.md` に基づいて出力されます。

```html
<p>公開日: {{ page.date | date: "%Y年 %m月%d日" }}</p>
<!-- page.date = 2020-01-01 00:00:00 +0900 -->
```
この場合は、次のように生成されます。

```html
<p>公開日: 2020年 01月 01日</p>
```

他にも様々な便利なフィルターがあります。
[こちら](http://jekyllrb-ja.github.io/docs/liquid/filters/) の公式Docs を見ておくといいかもしれません。

# Front Matter
Jekyll では Front Matter　を利用してページに変数を追加することができます。
ちなみに #1 で layout に post を指定しましたが、これも一種の変数で、
page.layout で得ることができます。

Front Matter は各 markdown ファイルや html ファイルの先頭に、`---`と`---`で挟んだ yaml 形式で書きます。
\#1 で Jekyll Admin 経由で生成した _posts/yyyy-mm-dd-hello-world を見て見ましょう。


```2020-05-19-hello-world.md
---
title: Hello, world!
layout: post
---

# Hello, world!
## こんにちは、世界！
**ｺﾝﾆﾁﾊ, ｾｶｲ**

ハロー、ワールド
```
先頭に `---`と`---` で挟まれたものがあります。
これが Front Matter です。

上位のレイアウトで、これを `page.title` や　`page.layout` で参照することができます。
また、この変数は独自に設定もできるため、例えば layout の下に `text: hello!` を追加すると、
`page.text` で参照でき、 "hello!" が出力されます。

# scss/sass を利用する

Jekyll ではデフォルトで scss/sass を利用できる機能があります。
プロジェクトルートに `_sass` ディレクトリを作成し、その配下に sass/scss ファイルを追加していきます。

また、プロジェクトルート配下の適当なところに index.scss を作成し、
作成したファイルを `@import` で読み込みます。（この時相対パスの起点は `_sass` ディレクトリです。

例えば `_scss` ディレクトリが以下のような場合、

```
.
├── _errors.scss
├── _global.scss
├── _index.scss
├── _navbar.scss
├── _page.scss
├── _post.scss
├── _variables.scss
└── bulma
    ├── ...
```

```index.scss
---
---

@import 'bulma/bulma';
@import 'bulma/bulma-tooltip/index';
@import 'variables';
@import 'global'; 
@import 'navbar';
@import 'index';
@import 'page';
@import 'post';
@import 'errors';
```

assets 配下に index.scss を作成し、
`<link href="{{ "/assets/index.css" | absolute_url }}" rel="stylesheet">` をレイアウトに追加することで、
読み込むことでできます。

# レイアウトを追加する

それではテーマ作りを始めていこうと思います。
前述の通りレイアウトを追加していくだけなので簡単です。

大きく分けると、

- 全てのレイアウトの基幹になる default レイアウト
- 投稿用の post レイアウト
- 固定（静的？）ページの page レイアウト

の3つがあるとだいたいきれいなサイトができます。

なお、あくまで操作やロジックの部分に焦点を当てていくので、デザインの部分はかなり手抜きします。
**[MVP.css](https://andybrewer.github.io/mvp/) を使用します。**
これは HTML タグからそのままスタイル適用してくれる css フレームワークです。

## default レイアウト

まずは全てのページの基幹となる default レイアウトを追加しましょう。
この辺りの実装方法は個人差がかなり出てくるものなのですが、
とりあえずは僕は default レイアウトからさらに別のレイアウトへと継承させていく感じをよくしています。

### ソースコード

`_layouts` ディレクトリに `default.html` を作成し、以下の通りにします。

```_layouts/default.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>
      {% if page.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %}
    </title>
    <meta name="description" content="{% if page.desc %}{{ page.desc }}{% else %}{{ page.content | strip_html | truncate: 130 }}{% endif %}">

    <!-- OGP の設定 -->
    <meta property="og:title" content="{% if page.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %}" />
    <meta property="og:type" content="article" />
    <meta property="og:url" content="{{ site.url }}{{ page.url }}" />
    <meta property="og:site_name" content="{{ site.title }}" />
    <meta property="og:description" content="{% if page.desc %}{{ page.desc }}{% else %}{{ page.content | strip_html | truncate: 130 }}{% endif %}" />
    <meta property="og:image" content="{% if page.thumbnail %}{{ page.thumbnail }}{% else %}{{ "/assets/images/ogp-image.png" | absolute_url }}{% endif %}" />

    <!-- RSSフィード の指定 -->
    <link rel="alternate" type="application/rss+xml" title="{{ site.title }} RSS" href="{{ "/feed.xml" | absolute_url }}" />

    <!-- mvp.css https://andybrewer.github.io/mvp/ を利用します。 MITライセンスで公開されています。  -->
    <link href="{{ "/mvp.css" | absolute_url }}" rel="stylesheet">
  </head>
  <body>
    {% include _header.html %}

    {{ content }}

    {% include _footer.html %}
  </body>
</html>
```

### 解説

```html
<title>
  {% if page.title %}{{ page.title }} - {{ site.title }}{% else %}{{ site.title }}{% endif %}
</title>
```

先ほどの説明の通りです。
これは、もし page.title に値が存在したら `{{ page.title }} - {{ site.title }}` を出力、
そうでければ `{{ site.title }}` を出力します。


```html
<meta name="description" content="{% if page.desc %}{{ page.desc }}{% else %}{{ page.content | strip_html | truncate: 130 }}{% endif %}">
```
これも同様に、 page.desc に値が存在したら  page.desc を、そうでなければ page.content (ページの内容が入った変数）の最初の 130字 を出力します。


次に、

```
 {% include _header.html %}
...
 {% include _footer.html  %}
```

liquid タグの include です。これはレイアウトを分割するのに非常に便利です。
`_include` 配下のファイルを読み込むことができます。

```_include/_header.html
<header>
  <nav>
      <a href="/">{{ site.title }}</a>
      <ul>
          <li><a href="/">ホーム</a></li>
      </ul>
  </nav>
  <h1>{{ page.title }}</h1>
</header>
```

最後に、

```
{{ content }}
```

これは読み込み元のソースを参照します。

## post レイアウト
### ソースコード

```_layouts/post.html
---
layout: default
---

<main>
  <article>
    <p>公開日: {{ page.date | date: "%Y年 %m月%d日" }}</p>
    {% if page.desc %}
    <h2>{{ page.desc }}</h2>
    {% endif %}
  
    <div>
      {{ content }}
    </div>
  </article>
</main>   
```

### 解説

```
---
layout: default
---
```

Front Matter　です。
基幹となる default レイアウトを指定しています。

```html
<p>公開日: {{ page.date | date: "%Y年 %m月%d日" }}</p>
```

日付のフォーマットです。
page.date で返ってくる値はあまりきれいなものでないので、
date フィルターを利用してきれいな形にしました。

他は特に特筆する点はないと思います。 if 文なども先ほど説明しました。

## page レイアウト
### ソースコード
```_layouts/page.html
---
layout: default
---

<main>
  <div>
    {% if page.desc %}
    <h2>{{ page.desc }}</h2>
    {% endif %}

    <div>
      {{ content }}
    </div>
  </div>
</main>
```

## 解説
特筆すべき事項は特にないですね。 post とだいたい一緒です。
front matter に layout を指定するのを忘れないようにしましょう。


# その他のページ作ってみる
## index.html
index.html がないと、ドメインのルートにアクセスした時に不格好なインデックスページが出てきてしまうので、
index.html を作りましょう。

```index.html
---
layout: default
---

<main>
  <section>
    <header>
      <h2>投稿一覧</h2>
    </header>

    {% for post in site.posts %}
      <aside>
        <a href="{{ post.url }}"><h3>{{ post.title }}</h3></a>
        <p>{% if post.desc %}{{ post.desc }}{% else %}{{ post.content | strip_html | truncate: 130 }}{% endif %}</p>
      </aside>  
    {% endfor %}
  </section>
</main>

```

### 解説
liquid タグの for を使っています。これは、 `for <変数名> in 配列` の形で、各要素を変数に取り出してくれます。


## 最終的なディレクトリ構成
```
.
├── 404.html
├── Gemfile
├── Gemfile.lock
├── _config.yml
├── _includes
│   ├── _footer.html
│   └── _header.html
├── _layouts
│   ├── default.html
│   ├── page.html
│   └── post.html
├── _posts
│   ├── 2020-05-19-hello-world.md
│   └── 2020-05-19-welcome-to-jekyll.markdown
├── index.html
└── mvp.css
```

# ソースコード全部
ソースコードを GithHub に公開しています。
https://github.com/yu-san-19/zero-starts-jektyll-nyumon/tree/7c75428c880be369a552bf72bf6a2f87d7a5293e

# 実行してみる

さて、一通りできたと思うので実行してみましょう。

```sh
$ bundle exec jekyll s
```

listening  的な感じのやつが出てきたら、以下にアクセスしてみましょう。
以下のように表示されたら成功です。

http://localhost:4000/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189712/4d46cf00-0cf5-db13-c0d4-b036436b3212.png)


http://localhost:4000/yyyy/mm/dd/hello-world.html (yyyy, dd, mm に適宜 mdファイル名の数字を入れる)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189712/c4ff9965-577f-9521-1b9f-88ff9c394576.png)


# さいごに

今回はテーマ作成ということで、レイアウトや liquid タグについて解説しました。
少々省略したり説明不足だったりしますが、日本語のドキュメントが結構しっかりと説明が書いてあったりしますので、是非読んでみてください。

ここがわからない、とか、ここは間違ってる、とか、こういう説明の方がわかりやすい、などありましたら気軽にコメントいただけたらありがたいです。

ここまでで（デザインなどは無視していますが）サイトは一応できています。
ですが、これでは誰でも見れる状態ではありません。
次回は、サイトの公開について、解説していこうと思います。

それではまた今度！