コンテンツにスキップ

テーマを作成してみよう

MkDocsでは、他の人が作ったテーマを使ったり、部分的にカスタマイズして使うことができますが、Jinja というテンプレートエンジンを使って、自分の作りたいように一から作ることもできます。ここでは、テーマを自作する方法を見ていきます。

自作テーマを使う設定

テーマに最低限必要なものは、main.html ファイルだけです。docs と同じ階層に別のフォルダを作って、その下にテーマ用のファイルを保存していきます。例えば、次のようにファイルを作ったとしましょう。

mkdocs.yml
docs/
    index.md
    about.md
custom_theme/
    main.html
    ...

custom_theme というフォルダを作ってその下にファイルを作っています。このフォルダ名は自分の好きなものにできます。このテーマを使うには、mkdocs.yml ファイルに次のように設定します。

theme:
    name: null
    custom_dir: 'custom_theme/'

テーマを null にし、custom_dir に自作テーマを置いている場所を指定します。これで、自作テーマの内容がサイトに反映されます。

custom_dir は、既存テーマの一部を上書きするためにも使われます。例えば、name: mkdocs と設定すると、mkdocs のテーマをベースとし、custom_dir 内で追加したファイルだけが上書きされてサイトに反映されます。

基本的なテーマ

以下では、具体的にテーマの作り方を見ていきます。

ただ、一から全部作るのは大変なので、テーマのひな型をダウンロードしておくといいでしょう。mkdocs-basic-theme からダウンロードできます。また、デフォルトで入っている2つのテーマ のファイルも参考になります。

main.html には、通常の HTML ファイルのように書いていきます。HTML ファイルのひな型に、最低限、次のようにしていけば、ページの表示ができます。

<title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>

head タグ内には、上のようにするとタイトルが表示されます。波かっこでくくられた部分は、Jinja によるものです。MkDocs のテーマは、Jinja というテンプレートエンジンを使っています。。

page.title はページタイトルで、ifendif は Jinja の文法です。波かっこ2つで表します。上のように書くと、ページタイトルがあれば、それが表示されます。config.site_name は、mkdocs.yml ファイルで設定した site_name のことです。

<body>
{{ page.content }}
</body>

body タグは、最低限これだけでもかまいません。page.content は、ページの内容です。Markdown で書いたものが HTML に変換されて表示されます。

このように、MkDocs で用意されたいくつかの変数と、Jinja の文法を組み合わせてテーマを作っていきます。

テーマに関連するファイル

テーマ内のファイルは、ファイルの種類によって、build 時の処理方法が異なります。

画像や CSS ファイルなどは、そのままコピーされます。

テーマ内の HTML ファイルは、テンプレートファイルとして扱われます。MkDocs は、このテンプレートファイルと docs フォルダの内容とを組み合わせて、各ページを作成します。

もし、HTML ファイルをそのままコピーしたい場合は、static_templates を使います。例えば、mkdocs テーマでは、次のように設定されています。

static_templates:
    - 404.html

このように設定すると、404.html ファイルは、変換されることなく、そのまま site フォルダにコピーされます。

テーマに関連するメタファイルは、コピーされません。テーマの設定が入った mkdocs_theme.yml ファイルや、Python ファイルは site フォルダにはコピーされません。また、 . から始まるファイルやフォルダも無視されます。

テンプレート変数

テンプレートを作る際には、いろいろな変数を使います。先ほど見た、page.contentpage.title などはその例です。テンプレート変数はたくさんあるのですべては紹介できないですが、いくつか見ていきます。

config

mkdocs.yml で設定した内容は、config 変数で呼び出せます。 config.site_name は先ほどの例でも出てきました。自分で新しく設定することもできますが、config.site_descriptionconfig.copyright などは、いろいろなテーマで使われるでしょう。

ナビゲーションに関する設定は、nav 変数を使います。例えば、次のように書きます。

{% if nav|length>1 %}
    <ul>
    {% for nav_item in nav %}
        {% if nav_item.children %}
            <li>{{ nav_item.title }}
                <ul>
                {% for nav_item in nav_item.children %}
                    <li class="{% if nav_item.active%}current{% endif %}">
                        <a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
                    </li>
                {% endfor %}
                </ul>
            </li>
        {% else %}
            <li class="{% if nav_item.active%}current{% endif %}">
                <a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a>
            </li>
        {% endif %}
    {% endfor %}
    </ul>
{% endif %}

nav は、複数の navigation から構成されています。

nav|length|length はフィルターといって、こう書くと要素数が取得できます。「nav の要素が1より大きければ」ということです。

{% for nav_item in nav %} は、nav にある各要素について処理する、ということです。各要素は nav_item にいれています。その次に {% if nav_item.children %} とありますが、これは、さらに下の階層があるかどうかを確認しています。

階層があればさらに下の階層を見ます。{% if nav_item.active%} は、今見ているページかどうかを判定しています。今見ているページなら、current クラスが追加されるので、さらに CSS で見た目を設定します。

{{ nav_item.url|url }} は、ページへのリンクです。|url は、これも先ほど出てきたフィルターと呼ばれるもので、こうするとパスを良い感じに調整してくれます。 {{ nav_item.title }} はページのタイトルです。

こうして、2階層下までのナビゲーションを表示することができます。

page

page オブジェクトに各ページの情報が入っており、それらすべてが pages に入っています。navdocs フォルダに入っているものよりも、mkdocs.yml で行った設定が優先されますが、pages は本当にすべての page が入っています。また、フォルダ・ファイルのアルファベット順になっています。なので、nav に含まれているものとは中身が違っていることもあります。

page には、page.title(ページタイトル)、page.content(コンテンツ)、page.url(ページへのリンク)といった、よく使うそうな属性が用意されています。例えば、次のような書き方ができます。

<a href="{{ page.url|url }}">{{ page.title }}</a>

|url フィルターにより、相対パスに変換できます。pages に対して行えば、全記事を一覧表示することができます。

目次を作ることもできます。Markdown ファイルで、h1 タグなどを使えば、自動的に id がつきます。英語ならうまく変換できるのですが、日本語の場合は変換されず、数字だけになってしまいます。この h* タグの情報は、page.toc に入っています。これを利用して、次のような書き方ができます。

<ul>
{% for toc_item in page.toc %}
    <li><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
    {% for toc_item in toc_item.children %}
        <li><a href="{{ toc_item.url }}">{{ toc_item.title }}</a></li>
    {% endfor %}
{% endfor %}
</ul>

2階層までの目次を表示します。 titleurl は、それぞれタイトルと URL です。 children はさらに下の階層を取得するためのものです。

page.meta を使えば、各ページのメタ情報を使うこともできます。次のように、Markdown ファイルの上の方でメタ情報の設定を行います。

---
mydata:
  - aaa
  - bbb
---
# ページタイトル

コンテンツ

YAML 記法でこのように書いたとしましょう。このとき、テンプレートに以下のように書けば、aaa, bbb がリストで表示されます。

<ul>
{% for myvalue in page.meta.mydata %}
  <li>myvalue</li>
{% endfor %}
</ul>

ナビゲーション関係では、前後の記事を表す page.previous_pagepage.next_page があり、親の階層を表す page.parent があります。最初のページの前のページや、最上階層の上の階層など、該当するものが存在しない場合は、None が返ります。

テンプレートフィルター

Jinja には、変数の値を表示する際に表記を変換する等の処理ができる、フィルター と呼ばれる機能があります。MkDocs ではJinja のフィルターも使用できますが、MkDocs で用意されているフィルターもあります。

すでに出てきていますが、url フィルターがあります。これは、絶対パスはそのまま表示し、相対パスで page から呼び出された場合は、そのページからの相対パスに変換されます。それ以外は、base_url がついたものになります。

tojson フィルターは、Python を JavaScript で使える値に変換します。

おわりに

ここでは、テーマを作るための流れや構成、使用できるテンプレート変数について見てきました。テーマを作るには、HTML の知識だけでなく、CSS や JavaScript の知識も必要で、Jinja についても知る必要があります。複雑なことをするには Python に関する知識も必要になってくるかもしれません。

次のページでは、Material テーマという、オススメのテーマについて見ていきます。そのまま使うこともできますし、テーマを自作したり、カスタマイズするのにも参考になるでしょう。