Creating a Custom Landing Page in Sphinx

Creating a Custom Landing Page in Sphinx

Sample code lives here. When I refer to source code line numbers, you're supposed to look at the code in this repository. Sample output (courtesy of readthedocs.org) is at https://sphinx-landing-template.readthedocs.io/en/latest/.

By convention the landing page for your Sphinx project is a simple table of contents. Nothing prevents you from adding a custom logo. Can you add a landing page like Jupyter has, with custom HTML and CSS, with full control on how this page is laid out? Yes you can.

I used sphinx-quickstart to create a boilerplate template for my code. I didn't bother configuring intersphinx or any extensions I didn't need. I did let Sphinx know that I want to use both .rst and .md suffixes, which is not necessary for this exercise.

I opted to separate source and build directories. I also amended the exclude_patterns config variable by adding ['_build']

The idea here is, that we move the table of contents to a separate file. Then we create a custom index page, by telling Sphinx to render the page index via our template index.html. In index.html we can use the full power of HTML. We will subclass the existing layout.html from the Alabaster theme we're using. If you desire this, you can go all out and design a customized version of a landing page, that does not inherit from layout.html.

Switching index.rst

If you used sphinx-quickstart, your index.rst contains the table of contents. Rename this file to contents.rst now. Make this change known to Sphinx by changing the property master_doc in conf.py to contents (line 45 in conf.py). Using the Alabaster theme, setting master_doc in conf.py will link the project title on the left top to our table of contents page.

Next we let Sphinx know that we still want to generate the index page, but with a different template. Add the following statement to your conf.py (line 113):

html_additional_pages = {'index': 'index.html'}

This will prompt Sphinx to render the page index using the template index.html. Since index.html is not a default template, the only way to find this template is in our projects source/_templates/ folder.

Creating index.html

Now create a file named index.html in source/_templates, this will be instantiated by Sphinx to create our index page. Sphinx templating is a topic of it's own. I'll describe what I did in this example.

The first line of index.html tells Jinja, the Sphinx template processor, to inherit from the template named layout.html. This gives us much of the basic layout that is provided by Alabaster and leaves us to fill in the title and body of this template.

The seoncd line of index.html sets the title. The underscore notation is used for I18N (Internationalization). This provides for a convenient entry point into translating your project later on.

The main part start in line three. We define the block body here, which supplants the block with the same name from the layout.html template in Alabaster. Double curly braces indicate Python-esque code to Jinja, while {% ... %} indicates directives to it. In both of these constructs we can use underscore notation for I18N. It's also possible to call functions that have been defined as part of the context of this template. You see the function pathto() being used. It will resolve a relative path in the project's source directory to a path that your browser understands.

pathto() accepts as arguments the path to a document, if this is an .rst document, don't use the .rst extension. Instead just write the name of the doc, without extension. You see this used in pathto("contents"), when we refer to the table of contents.

If you want to include images, refer to them like pathto('_static/images/...') in line 8 of index.html. This assumes that you place your images into the folder source/_static/images in your project's root folder.

Adding custom CSS

Custom CSS can be added by using a conf.py extension. This is a way of adding customization to Sphinx in a per-project manner. Define a conf.py extension by adding a setup(app) function to your conf.py. Sphinx will call it like any other setup(app) method that is bundled in a third party extension.

In line 172 of conf.py we use this extension point to inject a custom stylesheet into the Sphinx build:

def setup(app):
    app.add_stylesheet('css/custom.css')

You could do much more, but this suffices to add a custom CSS file to the output of the HTML processor. This CSS will be available in all of the project, including index.html.

Summary

The idea behind this article is to create custom landing pages. These should allow for a large degree of customization, while still tying into the Sphinx workflow. With the ability to use custom HTML and CSS you can ask a design minded person to create your landing page, without them needing to understand reStructured Text. I hope this is helpful for you when creating beautiful landing pages.

References

Jupyter landing page https://jupyter.readthedocs.io/en/latest/install.html
sphinx-quickstart https://www.sphinx-doc.org/en/master/usage/quickstart.html
Custom CSS or JavaScript https://docs.readthedocs.io/en/latest/guides/adding-custom-css.html
Sphinx templating https://www.sphinx-doc.org/en/master/templating.html
Jinja templates http://jinja.pocoo.org/

Image credits:

United States Geological Survey (USGS) [Public domain], via Wikimedia Commons
https://commons.wikimedia.org/wiki/File:Grand_Strand_Airport_-_South_Carolina.jpg

Follow me on Mastodon!