I’ve been struggling to write this next piece because I feel like I don’t actually know how to do this. But I’ll write what I do know, and I hope that it helps you!

There are two kinds of themes I’ve written for Sphinx: themes for HTML output and themes for PDF output. I understand the former better than the latter, but I’ll explain both.

HTML themes for Sphinx

The core piece of a Sphinx theme is a theme.conf INI file, which specifies things like what theme this theme inherits from, where the root stylesheet is, a set of variables used in the templates, etc.

For a very simple theme, all you need is that INI file and a CSS file with your specific bits of design. But you’ll probably want at least a root template and a root stylesheet for any real situation.

The INI file will look something like this:

[theme]
inherit = basic
stylesheet = css/main.css

[options]
some_variable = some_value

And you’ll want a layout.html file (written using Jinja) and a static/css/main.css next to it.

There are lots of blocks that the basic theme uses in layout.html, but the most crucial one is body. There’s also extrahead, menu, footer, etc.

Packaging and distributing HTML themes

The best way to get your HTML theme into someone else’s hands so they can use it (or, to be frank, to help you use and reuse it) is to make it and release it as a Python package. That’s something extensively covered elsewhere, but there is one gotcha: you have to be sure to include a few stanzas in your setup.py:

setup(
    package_data={"(your package name)": [
        "theme.conf",
        "*.html",
        "static/css/*.css",
        # ... etc
    ]},
    include_package_data=True,
    entry_points={
        "sphinx_themes": [
            "path = (your package name):template_path",
        ],
        "sphinx.html_themes": [
            "(your package name) = (your package name)",
        ],
    },
    # ... etc
)

More about this here.

PDF themes for Sphinx

This is where I don’t understand things as well. The basis of customizing your PDF output is to make a LaTeX style or class, which you can read about elsewhere. I’ve so far opted to make a style, because I want to build extensively on a pre-existing document class.

Now, assuming that the style you’ve defined is in the root of your documentation project, you can add the following to conf.py:

latex_additional_files = ["(your style).sty"]
latex_elements = {
    "preamble": r"""
        \usepackage{(your style)}
    """,
    # ... etc
}

Of course, styling a LaTeX document is easiest when you have access to the underlying markup directly, rather than just trying to apply styles to a bunch of semantic markup. But doing that would interfere with the point of using Sphinx, which is to describe your content semantically and use automated pipelines to build outputs. So as you work on your style, you may have to dip in to the generated TeX source to see how Sphinx has decided to emit things, but resist the temptation to edit there.

Further, I have no good solution at the moment for distributing your LaTeX styles for PDF output. I’d like, ideally, to include the .sty file in the Python package, but I don’t yet know how to make LaTeX find that as it builds, or alternatively make Sphinx put it in the build directory. If anyone does know, I’d appreciate some feedback!

For a working example of an HTML style distributed as a Python package, and with a LaTeX style thrown in, take a look at the one I’m working on, Murray.