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. On my Mac it’s: /Library/Python/2.7/site-packages/Sphinx-1.2b1-py2.7.egg/sphinx/themes/basic/, but Python on Mac OS X is just weird. If you’re using Linux (/usr/share/sphinx/themes/basic/ on Debian) or Windows, you should look it up yourself.

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'
Basic Theme
Basic theme without customization looks like vanilla HTML with no stylesheet whatsoever.

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 JavaScript or jQuerry (which is kindly included in the package).

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 jQuerry 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

Not the worst case, but still a valid comparison of TOC trees in Default theme and our JavaScript enhanced Basic. Note, that on the right the subcategories may be collapsed and opened dynamically.
Not the worst case, but still a valid comparison of TOC trees in Default theme and our JavaScript enhanced Basic.

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 algorithm 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 actually, 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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s