Writing custom Pelican themes

The built in configuration options in Pelican are fairly flexible, however Pelican can also be extended using custom themes. Following on from the previous post, this post is going to look at writing a simple custom theme for Pelican.

Using themes

By default Pelican uses the notmyidea theme, however it also ships with a simple theme. Both themes can be found in the themes directory. The THEME setting in penicanconf.py can be used to set a theme explicitly:

THEME = 'simple'

Alternatively the -t option can be used when the pelican command is run:

pelican -t some_theme -r $INPUTDIR -o $OUTPUTDIR -s $CONFFILE $PELICANOPTS

The first place you should look for additional themes is www.pelicanthemes.com. This provides a quick overview of the themes in the pelican-themes repository. To use a third party theme, just download it and use the THEME setting to point to the theme directory:

THEME = '/home/user/pelican/themes/example_theme'

Note: either absolute or relative paths can be used.

Using the pelican-themes command

Pelican comes with a pelican-themes command. This can be used to list available themes:

$ pelican-themes --list
notmyidea
simple

It's also possible to install new themes:

# Download and unpack a copy of the pelican-themes repo
curl -L -o pelican-themes.tar.gz https://github.com/getpelican/pelican-themes/archive/master.tar.gz
tar xzf pelican-themes.tar.gz

# Install a theme
pelican-themes --install pelican-themes-master/bricks/

Once a theme is installed it can be referenced by the THEME setting without requiring a path to the directory. Themes can also be removed using the --remove option:

pelican-themes --remove bricks

Theme structure

Pelican themes are made up of two directories, static and templates. The templates directory contains Jinja templates which are used to create HTML output; and the static directory contains any supporting static content such as style sheets or images.

The Pelican docs on creating themes list the following structure:

+-- static
¦   +-- css
¦   +-- images
+-- templates
    +-- archives.html         // to display archives
    +-- period_archives.html  // to display time-period archives
    +-- article.html          // processed for each article
    +-- author.html           // processed for each author
    +-- authors.html          // must list all the authors
    +-- categories.html       // must list all the categories
    +-- category.html         // processed for each category
    +-- index.html            // the index (list all the articles)
    +-- page.html             // processed for each page
    +-- tag.html              // processed for each tag
    +-- tags.html             // must list all the tags. Can be a tag cloud.

However you can get away with a subset of the templates above if you're not using a particular feature. For example if you've set ARCHIVES_SAVE_AS = '' the archives.html template will not be required.

Creating a custom theme

The Pelican docs on creating themes suggest starting with the "simple" theme . For the example in this post I'm going to run through an even more straightforward example which just covers the index.html and article.html templates. However you may want to dives straight into the simple theme if you're already happy with Jinja templates.

Pelican config

Initially it's often useful to focus on a subset of the require templates. The config below disables the tag, author, category, and archive pages generated by Pelican, and sets the theme to custom:

ARCHIVES_SAVE_AS = ''
AUTHOR_SAVE_AS = ''
AUTHORS_SAVE_AS = ''
CATEGORY_SAVE_AS = ''
CATEGORIES_SAVE_AS = ''
TAGS_SAVE_AS = ''

THEME = 'custom'

Note: interestingly CATEGORY_SAVE_AS and AUTHOR_SAVE_AS will both cause errors if they are set to None.

Folder structure

Running the following commands will set up an initial folder structure:

mkdir -p custom/{templates,static/css}
touch custom/templates/{index,article}.html
touch custom/static/css/main.css

The resulting structure should look something like this:

+-- custom
    +-- static
    |   +-- css
    |       +-- main.css
    +-- templates
        +-- article.html
        +-- index.html

From here it should be possible to run Pelican and produce output similar to the following:

    +-- theme
    |   +-- css
    |       +-- main.css
    +-- hello-world.html
    +-- index.html

Note: the generated files will initially be empty until the templates are updated.

Article template

A very simple article template might look something like this:

<!DOCTYPE html>
<html>
<head>
  <title>{{ article.title }}</title>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="/{{ THEME_STATIC_DIR }}/css/main.css">
</head>
<body>
  {{ article.content }}
</body>
</html>

Variables inside {{ }} will be replaced with content by Pelican. Variables such as article.title are taken from the article metadata, and THEME_STATIC_DIR is taken from the Pelican configuration file. The Pelican docs give more detail on available article attributes.

Index template

An index template that lists available articles could look something like this:

<!DOCTYPE html>
<html>
<head>
  <title>{{ SITENAME }}</title>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="/{{ THEME_STATIC_DIR }}/css/main.css">
</head>
<body>
  <h1>Articles</h1>
  <ul>
{% for article in articles_page.object_list %}
    <li><a href="/{{ article.url }}">{{ article.title }}</a> </li>
{% endfor %}
  </ul>
</body>
</html>

Code inside {% %} will be run when the template is rendered and will not be present in the final output. Again the Pelican theme docs have more info on available variables.

Template inheritance

The two templates above have a lot of shared content. To avoid duplication templates can be extended. The first thing to do is setup a base template called templates/base.html with content similar to the following:

<!DOCTYPE html>
<html>
<head>
  <title>{% block title %}{{ SITENAME }}{% endblock %}</title>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="/{{ THEME_STATIC_DIR }}/css/main.css">
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>

The block sections are used to mark content which can be overridden by templates which extend the base. It's worth noting that blocks can have defaults, for example the title above defaults to the value of SITENAME.

The article template above can now be changed to something similar to the following so that it extends the base template:

{% extends "base.html" %}

{% block title %}{{ article.title }}{% endblock %}
{% block content %}
  {{ article.content }}
{% endblock %}

The index template can also be simplified in a similar manner:

{% extends "base.html" %}

{% block content %}
  <h1>Articles</h1>
  <ul>
{% for article in articles_page.object_list %}
    <li><a href="/{{ article.url }}">{{ article.title }}</a> </li>
{% endfor %}
  </ul>
{% endblock %}