プログラミング

はじめに

hexo を紹介している日本語の記事は数あれど、テーマの作り方を解説している記事がなかったので、書いてみました。
私がテーマ制作時に困った事を中心に、hexo の自作テーマの作り方をまとめていきます。

同じようなところで詰まっている方の参考になればとっ。

--- 広告 ---

hexo のテーマの作り方

hexo テーマの作り方の手順は、ざっくり ▼ のような流れになります。

  1. hexo のインストール・セットアップ
  2. テーマディレクトリを用意
  3. css・js などを用意
  4. 最低限のテンプレートファイルを用意
  5. 共通パーツをまとめる
  6. 他のテンプレートファイルを用意

1. hexo のインストール・セットアップ

まず hexo がなければ何も始まりません。
hexo をインストールし、hexo initを実行しましょう。
hexo のプロジェクトディレクトリが出来上がります。

インストール方法はhexo - インストールをご覧ください。

2. テーマディレクトリを用意

hexo のプロジェクトディレクトリに、themeディレクトリがあるはずです。
そして、その中にはデフォルトテーマのlandscapeが入っていると思います。

landscapeと同階層に、自作テーマを入れるディレクトリを作ります。
ディレクトリ名はテーマの名前にすると良いでしょう。本記事では仮にmyblog-themeとします。

さらに、その中にlayoutsourceディレクトリと、_config.ymlを作ります。

全体像は ▼ のようになります。

1
themes
2
  ┣ landscape
3
  ┗ myblog-theme
4
    ┣ layout
5
    ┣ source
6
    ┗ _config.yml

各ディレクトリ・ファイルの役割ですが、

  • layoutには、記事のテンプレートファイルを入れます。
  • sourceには、css・js・画像などを入れます。
  • _config.ymlは、テーマの設定ファイルです。テーマに関する情報を記述します。

3. css・js などを用意

source ディレクトリの中に、テーマ用の css・js・画像を入れていきます。
本記事では、css フレームワークのBulmaを入れることにします。
Bulma - 公式ページの Download ボタンを押して、bulma.css を入手します。
ダウンロードできたら、source ディレクトリの直下に bulma.css を入れましょう。

必要に応じて、js や画像ファイルを source ディレクトリに格納してください。

4. 最低限のテンプレートファイルを用意

まず最低限のテンプレートファイルを用意し、ページを表示してみましょう。

hexo では EJS、 Pug、Haml などのテンプレートエンジンが使えます。お好みで変更すると良いです。
本記事では hexo のデフォルトの EJS を使って、テーマファイルを作っていきます。

まず layout ディレクトリ直下に、layout.ejs を用意し、下記のコードを記述しましょう。

▼layout.ejs

1
<!DOCTYPE html>
2
<html lang="ja">
3
  <head>
4
    <meta charset="utf-8">
5
    <title><%- config.title %></title>
6
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
7
    <meta name="viewport" content="width=device-width, initial-scale=1" />
8
    <%- css('bulma.css') %>
9
  </head>
10
  <body>
11
    <div class="wrapper">
12
      <main class="main">
13
        <%- body %>
14
      </main>
15
    </div>
16
  </body>
17
</html>

同じように layout ディレクトリ直下に index.ejs を用意し、下記のように記述します。

▼index.ejs

1
<p>theme test</p>

次に、_config.yml を変更します。
注意!変更するのは hexo プロジェクトのルートディレクトリ直下の_config.yml です!
themes/myblog-theme/_config.yml ではありません。

96 行目付近にtheme: landscapeとあると思います。
landscapeを自作テーマのディレクトリ名に変更しましょう。
今回はテーマディレクトリ名をmyblog-themeとしているので、theme: myblog-themeと書き換えます。


ここまでできたら、npm run build && npm run serverのコマンドを実行し、localhost:4000 をブラウザで開いてみましょう。
以下のように表示されると思います。
hexo theme 初期画面

とりあえず最小限のテーマファイルは制作完了。表示もできました。



さて、各ファイルを解説していきます。
ただその前に hexo の templates(テンプレートファイル)の構造について説明します。

hexo には大別して 6 種類のページが存在し、レンダリング時に各ページで合致する template が読み込まれ、描画する仕組みになっています。
以下がその 6 種類です。

Template Page Fallback
index Home page
post Posts index
page Pages index
archive Archives index
category Category archives archive
tag Tag archives archive

例えば、Home page(トップページ)は layout ディレクトリ内の index.ejs が読み込まれて、レンダリングされます。
記事を表示する post では、post.ejs が読み込まれて、レンダリングされます。
post.ejs がなければ index.ejs が読み込まれることになります。
このように各ページに対応する template が読み込まれ、レンダリング。該当する template がなければ、代わりの template が読み込まれるというのが hexo の templates の仕組みです。
WordPress のテーマと同じ構造ですね。

つまり、作りたいページに合わせて 1〜6 種類の template ファイルを制作していけば、hexo のテーマが出来上がるわけです。
(※index だけは必ず作らなければなりません。)


次に、layout.ejs についてです。
layout.ejs は、上記の 6 つの tamplate とは異なる特殊なファイルになります。
layout.ejs は、サイト全体で共通化させたい要素を入れる template ファイルになります。

例えば、header や、footer など…サイト全体で共通の要素を、各 template に記述するのは面倒ですよね。
そういったサイト全体で共通の要素やレイアウトは layout.ejs に記述しておきます。
併せて、<%- body %>も忘れずに layout.ejs に記述しておきます。

こうすることで…

  1. 各ページが読み込まれる時に、layout.ejs が読み込まれる。
  2. <%- body %>が各テンプレートに置き換わる。
    トップページなら index.ejs の中身が<%- body %>に入る。

といった処理が hexo 内で行われ、サイト全体で要素の共通化できます

layout.ejs は複数用意できたりなど…いろいろカスタマイズが可能ですので、一度公式の hexo templatesをご確認ください。


さて、実際のファイルの中身を見ていきましょう。
まず layout.ejs ですが、中身はなんの変哲もない EJS です。
解説が必要なのは、下記の 3 行でしょう。

1
<title><%- config.title %></title>
2
<%- css('bulma.css') %>
3
<%- body %>

<title><%- config.title %></title>

<title>タグの中身ですが、config.title とありますね。
このように記述することで、_config.yml(hexo プロジェクトのルートディレクトリ直下の_config.yml) にある title の情報を表示しています。
<%- config.subtitle %>と変更すれば、サブタイトルを表示できます。
config.XXXで、設定ファイルの情報を取得することができます。

config の他にも、hexo にはいくつかの変数が用意されており、カンタンに使うことができます。
hexo の変数一覧はhexo - Variablesをご覧ください。


<%- css('bulma.css') %>
css()は hexo のヘルパーの一種です。
引数に css ファイルへのパスを記述することで、自動的に<link rel="stylesheet" href="style.css">と展開してくれます。
(パスは、テーマディレクトリの source をルートとします。)
このヘルパーを使って、さきほど格納した bulma.css を読み込んでいるわけです。

css()の他にも、js ファイル読み込み用のjs()や、引数をルートパスにして出力するurl_for()など、便利なヘルパーがたくさん揃っています。
hexo のヘルパー一覧はhexo - Helpersをご覧ください。


<%- body %>
こちらは、先程チラッと解説しましたね。
layout.ejs に、<%- body %>と記述した部分が各 template に置き換わります。
現在は、index.ejs が読み込まれて、<p>theme test</p>に置き換わっています。


ここまでで最低限の template が用意できました。
次に、サイト全体で共通となるパーツをまとめていきましょう。

5. 共通パーツをまとめる

まずはサイト全体で共通となる要素を整理していきます。
本記事では、header、footer をサイト全体で共通化することにします。

myblog-theme ディレクトリ内を以下のようにしましょう。
_partial の中に共通化するパーツを入れていきます。

1
myblog-theme
2
  ┣ layout
3
  ┃    ┣ layout.ejs
4
  ┃    ┗ index.ejs
5
6
  ┣ _partial ←追加
7
  ┃    ┣ header.ejs ←追加
8
  ┃    ┗ footer.ejs ←追加
9
10
  ┣ source
11
  ┃    ┗ bulma.css
12
13
  ┗ _config.yml

ファイルを作成後、それぞれ中身を記述していきましょう。

▼layout.ejs

1
<!DOCTYPE html>
2
<html lang="ja">
3
  <head>
4
    <meta charset="utf-8">
5
    <title><%- config.title %></title>
6
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
7
    <meta name="viewport" content="width=device-width, initial-scale=1" />
8
    <%- css('bulma.css') %>
9
  </head>
10
  <body>
11
    <div class="wrapper">
12
      <%- partial('_partial/header') %> ←追加
13
      <main class="main">
14
        <%- body %>
15
      </main>
16
      <%- partial('_partial/footer') %> ←追加
17
    </div>
18
  </body>
19
</html>

▼_partial/index.ejs

1
<section class="hero is-success is-fullheight-with-navbar">
2
  <div class="hero-body">
3
    <div class="container">
4
      <p class="title">
5
        <%- config.title %> - トップページ
6
      </p>
7
      <a class="button is-success is-inverted has-text-weight-bold" href="<%- config.root %><%- config.archive_dir %>">記事一覧ページへ</a>
8
    </div>
9
  </div>
10
</section>

▼_partial/header.ejs

1
<header>
2
  <nav class="navbar" role="navigation" aria-label="main navigation">
3
    <div class="navbar-brand">
4
      <a class="navbar-item" href="<%- config.root %>">
5
        <%- config.title %>
6
      </a>
7
      <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
8
        <span aria-hidden="true"></span>
9
        <span aria-hidden="true"></span>
10
        <span aria-hidden="true"></span>
11
      </a>
12
    </div>
13
  </nav>
14
</header>

▼_partial/footer.ejs

1
<footer class="footer has-background-black-ter">
2
  <div class="content has-text-centered">
3
    <p class="has-text-white-ter">
4
      <%- config.title %>
5
    </p>
6
  </div>
7
</footer>

_partial ディレクトリ内に、header.ejs、footer.ejs を用意しました。
この 2 つを layout.ejs にて<%- partial('_partial/XXXX') %>を使って読み込み、サイト全体で、header と footer を共通化しています。

▼ 画面は下記のようになったと思います。
hexo 初期レイアウト構築

partial()は hexo に用意された機能で、パーツ分けしたファイルを読み込むことができます。
ページ間で使いまわしたい要素は 1 ファイルにまとめておき、partial()で読み込むようにすると良いです。
共通パーツを、よりカンタン・効率的に再利用できます。

また、<%- partial('partial/header', {title: 'Hello World'}) %>のように、partial()の第二引数に、オブジェクトを入れることで読み込むファイルに変数を渡すことができます。
上記の例では、header.ejs で<%- title %>と記述すれば、Hello World と表示されるはずです。

6. 他のテンプレートファイルを用意

この章では、以下の 4 つの template を追加し、テーマを作り込んでいきます。

  • 記事・固定ページの内容を表示する _partial/article.ejs
  • 記事一覧を表示する archive.ejs
  • 固定ページ用テンプレート page.ejs
  • 記事用テンプレート post.ejs

▼ テーマファイルの全体的な構成は、以下の通りです。

1
myblog-theme
2
  ┣ layout
3
  ┃    ┣ layout.ejs
4
  ┃    ┣ index.ejs
5
  ┃    ┣ archive.ejs ←追加
6
  ┃    ┗ page.ejs ←追加
7
  ┃    ┗ post.ejs ←追加
8
9
  ┣ _partial
10
  ┃    ┣ article.ejs ←追加
11
  ┃    ┣ header.ejs
12
  ┃    ┗ footer.ejs
13
14
  ┣ source
15
  ┃    ┗ bulma.css
16
17
  ┗ _config.yml

▼archive.ejs

1
<section class="section">
2
  <div class="container">
3
    <div class="columns is-multiline is-mobile">
4
      <% site.posts.each(function(post){ %>
5
        <div class="column is-12-mobile is-4-tablet">
6
          <div class="box">
7
            <a href="<%- config.root %><%- post.path %>">
8
              <p class="is-size-4"><%- post.title %></p>
9
              <p><%- post.date.format(config.date_format) %></p>
10
            </a>
11
          </div>
12
        </div>
13
      <% }); %>
14
    </div>
15
  </div>
16
</section>
17
18
<section class="section">
19
  <div class="container">
20
    <nav class="pagination" role="navigation" aria-label="pagination">
21
      <% if (page.prev){ %>
22
        <a class="pagination-previous" href="<%- config.root %><%- page.prev_link %>">Previous</a>
23
      <% } %>
24
      <% if (page.next){ %>
25
        <a class="pagination-next" href="<%- config.root %><%- page.next_link %>">Next page</a>
26
      <% } %>
27
    </nav>
28
  </div>
29
</section>

archive.ejs は、記事一覧を表示するテンプレートです。

hexo に投稿した記事は、site.posts で全て取得することができます。
site.posts を each 文で回し、それぞれの記事が持っている情報を出力しています。
記事のタイトルは<%- post.title %>で出力。
リンクは<%- config.root %><%- post.path %>を組み合わせて、記事へのルートパスとして出力。
記事の投稿時間は<%- post.date.format(config.date_format) %>で出力しています。

▼ 記事一覧ページは下記のようになります。
hexo 記事一覧画面

記事が 1 つだけなので、ページに表示されていませんが、記事一覧の下にページネーションも実装しています。
page.prev および page.next でそれぞれ、最初のページか 最後のページか を判定しています。
そして、<%- page.prev_link %> で前のページへのリンクを。<%- page.next_link %> で次のページへのリンクを出力しています。

本記事で制作したページネーションはボタン 2 つのシンプルなモノです。
hexo では、page.totalで記事の総数を取得できたり、page.currentで現在のページの位置を取得できたりしますので、組み合わせてページネーションをカスタマイズしてみると良いでしょう。
hexo の変数一覧はhexo - Variablesをご覧ください。

また、hexo では、プロジェクトのルートディレクトリ直下の_config.ymlindex_generatorという項目があり、その中にper_pageなるモノがあります。
per_pageの値が Archives ページ(記事一覧ページ)で表示する記事の数になっています。初期値は 10 です。
この値を変更する事で 1 ページあたりに表示する記事の数を変更することができます。

ちなみに、、hexo でページネーションをカンタンに出力できるプラグインがあります。
ページネーション自作するの面倒くさいという方は導入を検討してみてください。
> hexojs/hexo-pagination

▼_partial/article.ejs

1
<section class="hero is-success">
2
  <div class="hero-body">
3
    <div class="container">
4
      <p class="title">
5
        <%- page.title %>
6
      </p>
7
    </div>
8
  </div>
9
</section>
10
11
<section class="section">
12
  <div class="container">
13
    <div class="box">
14
      <div class="content">
15
        <%- page.date.format(config.date_format) %>
16
        <hr>
17
        <%- page.content %>
18
      </div>
19
    </div>
20
  </div>
21
</section>

_partial/article.ejs は、hexo に投稿した記事を表示するためのファイルです。
<%- page.title %>で、記事のタイトルを出力。
<%- page.date.format(config.date_format) %>は、記事の投稿日時を出力。
<%- page.content %>で、記事の内容を出力します。

_partial/article.ejs は、記事ページ(post.ejs)と固定ページ(page.ejs)で共通となるため、_partial に格納しました。

post.ejs と page.ejs で、以下のように_partial/article.ejs を読み込みましょう。


▼post.ejs / page.ejs(※2 ファイルとも中身は同一。)

1
<%- partial('_partial/article', { page : page }) %>

▼ 記事の画面は、下記のようになります。
hexo 記事画面



シンプルではありますが、hexo のテーマを作り上げることができました!
トップページから記事一覧ページ、それぞれの記事ページと固定ページまで、表示されるようになっています。
hexo newのコマンドを実行し、記事や固定ページを追加していきましょう。
投稿した記事が、記事一覧ページや記事ページに表示されると思います。

最後に

hexo テーマ開発は学習コストも少なく、設定や変数を組み合わせる事でカンタンにテンプレートやパーツを作ることができます。
下記のページを参考に、ご自身の hexo テーマを作ってみてください!
hexo - Configuration
hexo - Variables

では、また!