Humble Collection of Python Sphinx Gotchas: Part I
Gotcha 1: Getting Started with Sphinx Theming
Update 02.10.2014: It seems absolutely obvious to me, but apparently it is not that obvious to some: you can’t do anything about your Sphinx theme without some basic CSS and HTML skills. You can only change some of the theme parameters, if the developer was kind enough to add some, but it is well covered in the official documentation.
Customizing Sphinx visuals was somewhat upsetting to me at first, since the process is not straightforward and documentation is scarce. It could be a little bit user-friendlier. However, as soon as you get a grip on the basics, it gets pretty smooth and simple. You may have already tried copying over the default theme only to discover, that there is nothing particularly useful in there for an inquisitive scholar. Only one of the standard Sphinx themes is in fact complete, the rest of them simply inherit its properties to add some minor alterations. This theme is called Basic and it’s a minimal sandbox template, the only theme that could be helpful for getting to the very bottom of Sphinx customization. Later you’d be able to inherit it and create a template, consisting only of alterations, but for a start it’s OK to copy the Basic in its entirety.
Hopefully, you’ve already created a folder for your Sphinx project and initiated it by issuing:
$ sphinx-quickstart
Or you may have an already existing Sphinx project, you want to theme — it’s up
to you. In your project folder create the _themes
directory and then rename
and copy the theme folder there. Basic theme, perhaps, should be located in
site_packages
folder of your active Python install.
Next step would be to change conf.py
of your project, accordingly. First, we
should make sure, that the following line is uncommented and correct:
html_theme_path = ['_themes']
Don’t forget to check the value of the html_theme
parameter above:
html_theme = 'renamed_basic'
As of now you may start making alterations to the theme. You will find that
HTML files in there aren’t really HTML files, but templates (Sphinx uses
Jinja template engine)
with some staff automatically inserted on build. You can combine these
automatic tags with basic HTML tags and as soon as you figure out how it works,
you could move some of the interactive tags around, or get rid of some of them
altogether. Don’t forget to check whether you’re not breaking anything though.
Most visual aspects of Sphinx theme are modified through the main CSS file,
which is located in the static
folder. For Basic theme it would be
basic.css_t
. Notice, that t
in the extension brings to our attention the
fact, that this is a template. Other than that it could be viewed and edited as
a simple CSS file. If you’re interested in the values, provided by Sphinx
templates and how you cam make use of them, consult the official
documentation. If you
want the main CSS file to have some other name, you can change that in
theme.conf
, there are also some other settings, that could be of interest.
Gotcha 2: Spoiler
If you play with Sphinx templates for some time, you may want to start adding something more interactive. We could easily implement these kinds of things trough plain JavaScript or jQuery.
Let’s go trough a little example project, to recognize, what could be achieved
with combined power of Sphinx and JavaScript. You may notice, that as for
common elements in HTML themes there is usually a way to address them in
JavaScript, it’s not that easy to introduce new classes and wrap some parts of
the text into them. There is no custom div
tag in Sphinx, unless you do that
as .. raw
directive which is not really a native way, and breaks lots of
things. What I needed to do was to wrap some section of text into a div
and
make it collapsible on button pressed. You could achieve that by creating your
own admonition and assigning it to certain HTML class.
For example:
.. admonition:: Request
:class: splr
Request example and parameters.
You can then easily reference this admonition by its class in your JavaScript.
For example you could put this jQuery script to your page.html
template:
<script type="text/javascript">
$('.splr').css("background-color", '#F0F0F0');
$('.splr').css('box-shadow', '0px 0px 1px 1px #000000');
$('.splr > .last').hide();
$('.splr > .expanded + .last').show('normal');
$('.splr > p').click(function() {
$(this).find('.last').hide();
$(this).toggleClass('expanded').toggleClass('collapsed').next('.last').toggle('normal');
});
</script>
If implemented correctly, this script should turn the splr
admonition into a
collapsible drawer. I’ve been actively using this, when documenting HTTP APIs,
since it’s very helpful to hide JSON responses by default. Note, that you could
also use this method for special CSS effects. Imagine, if aside from usual
note, warning and tip you could have yellow, blue and purple boxes for whatever
reason you can think of? Well, it couldn’t be any simpler. Admonitions are good
default containers for some parts of your text, that should differ in design or
function from the rest of the page.
Gotcha 3: Interactive TOC in Sidebar
You may have noticed, that Sphinx often adds TOC to sidebar automatically, if it’s not explicitly placed in the page itself. While this is certainly a very useful feature, sometimes things get out of control. I didn’t use the worst case in the picture on the right, but it could get up to innumerable 1st level sections, each of them could have a number of subsections and so on. Sometimes it is a clear sign, that the page should be reorganized into multiple standalone pages, united by a category, but it’s not always possible or needed. It’s perfectly alright to have a long and deep TOC in some cases and Default Sphinx theme is terrible in that regard (as of 2014 it is fixed and children categories are shown only for the currently selected one).
You could use an updated version of the algorithm from the previous example to
collapse some parts of the TOC by default. Note, how this script works with
ul
and li
tags of the TOC tree list. Some stuff is applied recursively on
the highest level of the list, some — on the subsequent levels. Especially
noticeable with different styles applied to different levels of the list, so
that you could tell whether it is 1st, 2nd or even 3rd level title. Here is the
full script:
<script type="text/javascript">
$('.sphinxsidebar').attr('position', 'fixed');
$('.sphinxsidebar').css('position', 'fixed');
$("h3:contains('Table Of Contents')").css('border-bottom', '1px');
$("h3:contains('Table Of Contents')").text('TOC:');
$('.sphinxsidebarwrapper li').css('background-color', '#B8B8B8');
$('.sphinxsidebarwrapper li').css('box-shadow', '0px 0px 1px 1px #000000');
$('.sphinxsidebarwrapper li').css('color', 'white');
$('.sphinxsidebarwrapper li').css('border-color', '#000000');
$('.sphinxsidebarwrapper li > ul > li').css('background', '#D0D0D0');
$('.sphinxsidebarwrapper li > ul > li').css('box-shadow', '0px 0px 1px 1px #000000');
$('.sphinxsidebarwrapper li > ul > li > ul > li').css('background', '#F0F0F0');
$('.sphinxsidebarwrapper li > ul').hide();
$('.sphinxsidebarwrapper li > .expanded + ul').show('normal');
$('.sphinxsidebarwrapper li > a').click(function() {
//hide everything
$(this).find('li').hide();
//toggle next ul
$(this).toggleClass('expanded').toggleClass('collapsed').next('ul').toggle('normal');
});
</script>
Perhaps, it won’t look very well, but it is a very simplified version to
illustrate the concept. If you get the idea, you may alter or add .css
methods to achieve more plausible visuals. You could also work on lower title
levels, but you will have to figure it all out for yourself. In this version
the snippet works best only with three headings levels from <h1>
to <h3>
,
but it could definitely handle more.
That’s it. I’ve compiled these
examples as a full-featured theme project on GitHub. I’m going to polish it to
some extent and perhaps, implement more interactive stuff over time. Feel free
to contribute to this little project. (Nope, never happened. Well, it did
happened, but it was no good, sorry.) If you come upon any issue with this
little gotchas of mine, let me know. Sure, I’ve tested everything myself, but
you never know. Also, you’re free to use any of these examples as a building
block in your work with no attribution, since they’re rather generic and simple.