+ +

Custom themes

+

A guide to creating and distributing custom themes.

+
+

When creating a new theme, you can either follow the steps in this guide to +create one from scratch or you can download the mkdocs-basic-theme as a +basic, yet complete, theme with all the boilerplate required. You can find +this base theme on GitHub. +It contains detailed comments in the code to describe the different features +and their usage.

+

Creating a custom theme

+

The bare minimum required for a custom theme is a base.html Jinja2 +template file. This should be placed in a directory which will be the +theme_dir and it should be created next to the mkdocs.yml configuration +file. Within mkdocs.yml, specify the theme_dir option and set it to the +name of the directory containing base.html. For example, given this example +project layout:

+
mkdocs.yml
+docs/
+    index.md
+    about.md
+custom_theme/
+    base.html
+    ...
+
+

You would include the following setting to use the custom theme directory:

+
theme_dir: 'custom_theme'
+
+

If used in combination with the theme configuration value a custom theme can +be used to replace only specific parts of a built-in theme. For example, with +the above layout and if you set theme: mkdocs then the base.html file +would replace that in the theme but otherwise it would remain the same. This +is useful if you want to make small adjustments to an existing theme.

+

Basic theme

+

The simplest base.html file is the following:

+
<!DOCTYPE html>
+<html>
+  <head>
+    <title>{% if page_title %}{{ page_title }} - {% endif %}{{ site_name }}</title>
+  </head>
+  <body>
+    {{ content }}
+  </body>
+</html>
+
+ +

Article content from each page specified in mkdocs.yml is inserted using the +{{ content }} tag. Stylesheets and scripts can be brought into this theme as +with a normal HTML file. Navbars and tables of contents can also be generated +and included automatically, through the nav and toc objects, respectively. +If you wish to write your own theme, it is recommended to start with one of +the built-in themes and modify it accordingly.

+

Template Variables

+

Each template in a theme is built with a template context. These are the +variables that are available to themes. The context varies depending on the +template that is being built. At the moment templates are either built with +the global context or with a page specific context. The global context is used +for HTML pages that don't represent an individual Markdown document, for +example a 404.html page or search.html.

+

Global Context

+

The following variables in the context map directly the configuration options +.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Variable NameConfiguration name
site_namesite_name
site_authorsite_author
faviconsite_favicon
page_descriptionsite_description
repo_urlrepo_url
repo_namerepo_name
site_urlsite_url
extra_cssextra_css
extra_javascriptextra_javascript
extraextra
include_navinclude_nav
include_next_previnclude_next_prev
copyrightcopyright
google_analyticsgoogle_analytics
+

The following variables provide information about the navigation and location.

+ +

The nav variable is used to create the navigation for the documentation. +Following is a basic usage example which outputs the first and second level +navigation as a nested list.

+
<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 }}">{{ nav_item.title }}</a>
+                  </li>
+              {% endfor %}
+              </ul>
+          </li>
+      {% else %}
+          <li class="{% if nav_item.active%}current{%endif%}">
+              <a href="{{ nav_item.url }}">{{ nav_item.title }}</a>
+          </li>
+      {% endif %}
+
+  {% endfor %}
+</ul>
+
+ +

base_url

+

The base_url provides a relative path to the root of the MkDocs project. +This makes it easy to include links to static assets in your theme. For +example, if your theme includes a js folder, to include theme.js from that +folder on all pages you would do this:

+
<script src="{{ base_url }}/js/theme.js"></script>
+
+ +

homepage_url

+

Provides a relative path to the documentation homepage.

+

mkdocs_version

+

Contains the current MkDocs version.

+

build_date_utc

+

A Python datetime object that represents the date and time the documentation +was built in UTC. This is useful for showing how recently the documentation +was updated.

+

Page Context

+

The page context includes all of the above Global context and the following +additional variables.

+

page_title

+

Contains the Title for the current page.

+

page_description

+

Contains the description for the current page on the homepage, it is blank on +other pages.

+

content

+

The rendered Markdown as HTML, this is the contents of the documentation.

+

toc

+

An object representing the Table of contents for a page. Displaying the table +of contents as a simple list can be achieved like this.

+
<ul>
+{% for toc_item in 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>
+
+ +

meta

+

A mapping of the metadata included at the top of the markdown page. In this +example we define a source property above the page title.

+
source: generics.py
+        mixins.py
+
+# Page title
+
+Content...
+
+ +

A template can access this metadata for the page with the meta.source +variable. This could then be used to link to source files related to the +documentation page.

+
{% for filename in meta.source %}
+  <a class="github" href="https://github.com/.../{{ filename }}">
+    <span class="label label-info">{{ filename }}</span>
+  </a>
+{% endfor %}
+
+ +

canonical_url

+

The full, canonical URL to the current page. This includes the site_url from +the configuration.

+

current_page

+

The page object for the current page. The page path and url properties can be +displayed like this.

+
<h1>{{ current_page.title }}</h1>
+<p> This page is at {{ current_page.url }}</p>
+
+ +

previous_page

+

The page object for the previous page. The usage is the same as for +current_page.

+

next_page

+

The page object for the next page.The usage is the same as for current_page.

+

Extra Context

+

Additional variables can be passed to the template with the +extra configuration option. This is a set of key value +pairs that can make custom templates far more flexible.

+

For example, this could be used to include the project version of all pages +and a list of links related to the project. This can be achieved with the +following extra configuration:

+
extra:
+    version: 0.13.0
+    links:
+        - https://github.com/mkdocs
+        - https://docs.readthedocs.org/en/latest/builds.html#mkdocs
+        - http://www.mkdocs.org/
+
+ +

And then displayed with this HTML in the custom theme.

+
{{ config.extra.version }}
+
+{% if config.extra.links %}
+  <ul>
+  {% for link in config.extra.links %}
+      <li>{{ link }}</li>
+  {% endfor %}
+  </ul>
+{% endif %}
+
+ +

Search and themes

+

As of MkDocs 0.13 client side search support has been added to MkDocs with +Lunr.js.

+

Search can either be added to every page in the theme or to a dedicated +template which must be named search.html. The search template will be build +with the same name and can be viewable with mkdocs serve at +http://localhost:8000/search.html. An example of the two different +approaches can be seen by comparing the mkdocs and readthedocs themes.

+

The following HTML needs to be added to the theme so the JavaScript is loaded +for Lunr.js.

+
<script>var base_url = '{{ base_url }}';</script>
+<script data-main="{{ base_url }}/mkdocs/js/search.js" src="{{ base_url }}/mkdocs/js/require.js"></script>
+
+ +
+

Note

+

The above JavaScript will download the search index, for larger +documentation projects this can be a heavy operation. In those cases, it +is suggested that you either use the search.html approach to only +include search on one page or load the JavaScript on an event like a form +submit.

+
+

This loads the JavaScript and sets a global variable base_url which allows +the JavaScript to make the links relative to the current page. The above +JavaScript, with the following HTML in a search.html template will add a +full search implementation to your theme.

+
<h1 id="search">Search Results</h1>
+
+<form action="search.html">
+  <input name="q" id="mkdocs-search-query" type="text" class="search_input search-query ui-autocomplete-input" placeholder="Search the Docs" autocomplete="off">
+</form>
+
+<div id="mkdocs-search-results">
+  Sorry, page not found.
+</div>
+
+ +

This works by looking for the specific ID's used in the above HTML. The input +for the user to type the search query must have the ID mkdocs-search-query +and mkdocs-search-results is the directory where the results will be placed.

+

Packaging Themes

+

MkDocs makes use of Python packaging to distribute themes. This comes with a +few requirements.

+

To see an example of a package containing one theme, see the MkDocs Bootstrap +theme and to see a package that contains many themes, see the MkDocs +Bootswatch theme.

+

Package Layout

+

The following layout is recommended for themes. Two files at the top level +directory called MANIFEST.in amd setup.py. Then a directory with the name +of your theme and containing a base.html file and a __init__.py.

+
.
+|-- MANIFEST.in
+|-- theme_name
+|   |-- base.html
+|   |-- __init__.py
+`-- setup.py
+
+ +

The MANIFEST.in file should contain the following contents but with +theme_name updated and any extra file extensions added to the include.

+
recursive-include theme_name *.ico *.js *.css *.png *.html *.eot *.svg *.ttf *.woff
+recursive-exclude * __pycache__
+recursive-exclude * *.py[co]
+
+ +

The setup.py should include the following text with the modifications +described below.

+
from setuptools import setup, find_packages
+
+VERSION = '0.0.1'
+
+
+setup(
+    name="mkdocs-themename",
+    version=VERSION,
+    url='',
+    license='',
+    description='',
+    author='',
+    author_email='',
+    packages=find_packages(),
+    include_package_data=True,
+    entry_points={
+        'mkdocs.themes': [
+            'themename = theme_name',
+        ]
+    },
+    zip_safe=False
+)
+
+ +

Fill in the URL, license, description, author and author email address.

+

The name should follow the convention mkdocs-themename (like mkdocs- +bootstrap and mkdocs-bootswatch), starting with MkDocs, using hyphens to +separate words and including the name of your theme.

+

Most of the rest of the file can be left unedited. The last section we need to +change is the entry_points. This is how MkDocs finds the theme(s) you are +including in the package. The name on the left is the one that users will use +in their mkdocs.yml and the one on the right is the directory containing your +theme files.

+

The directory you created at the start of this section with the base.html file +should contain all of the other theme files. The minimum requirement is that +it includes a base.html for the theme. It must also include a +__init__.py file which should be empty, this file tells Python that the +directory is a package.

+

Distributing Themes

+

With the above changes, your theme should now be ready to install. This can be +done with pip, using pip install . if you are still in the same directory as +the setup.py.

+

Most Python packages, including MkDocs, are distributed on PyPI. To do this, +you should run the following command.

+
python setup.py register
+
+ +

If you don't have an account setup, you should be prompted to create one.

+

For a much more detailed guide, see the official Python packaging +documentation for Packaging and Distributing Projects.

+

https://packaging.python.org/en/latest/distributing.html