02. 04. 2021 Mattia Codato Development, NetEye

Advanced Sphinx Theme Customization

In a companion post we talked about how to create a custom theme for Sphinx, like the one we made for the new NetEye online user guide.
Sometimes just a basic customization is not enough and you want to create something much more advanced. In this article, we’ll look at some more detailed custom parts.

Custom Landing Page

By default, the home page in a Sphinx project is represented by the index.rst file which can contain for instance a title and a ToC tree directive.

Certainly for a basic home page, having just a default index.rst is enough, but in our case we wanted to add some HTML code to create a richer landing page.

Anyone can achieve the same type of result simply by following these steps:

1. In the custom theme structure, create an index.html file inside a folder called templates/ like this:

neteye
  ├── footer.html
  ├── globaltoc.html
  ├── layout.html
  ├── templates
  │   └── index.html
  └── theme.conf

2. Customize the new index page:

The index.html is basically like the layout.html page. We can change any block we want. For example, to avoid starting from scratch we can extend our theme’s layout.html file:

{% extends "layout.html" %}

Then we can create a CSS file for the landing page and link to it:

 {% set css_files = css_files + ['_static/neteye-home.css'] %}

At this point, say we’d like to add a banner, so let’s override the sidebar1 block:

{%- block sidebar1 %}
<div id="menu" class="col-md-3 col-xl-2">
    {{ sidebar() }}
</div>

<div id="content" class="col">
    <div id="index-banner" class="jumbotron jumbotron-fluid">
        <h1 class="display-4">{{ title|striptags|e }}</h1>
        <p>System Management with NetEye is your answer to the challenges of digital transformation.</p>
    </div>
    <div class="row" id="document">
        <div class="col">
{% endblock %}

And we want to remove the right column with the local ToC:

{% block leftColumn %}{% endblock %}

3. At this point the only thing missing is to point to index.html in the conf.py file:

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

Scrollspy on the Local ToC

Another advanced section that we want in our documentation is a local ToC that highlights the current chapter, and that changes as the user scrolls.
To build this element, Bootstrap can help us with its scroll spy functionality.

Unfortunately, the default ToC on Sphinx is made up of ul and li elements, while Bootstrap supports the scroll spy only on the nav element or group list. It’s clear that we need to replace the HTML structure of the local ToC:

  1. First of all, we create a new localtoc.html file
  2. Then we want to replace the default structure with a nav component. For this purpose we decided to use the replace function:
{% set tocNav = toc
| replace("<li>", "")
| replace("</li>", "")
| replace("<ul>", "<nav class='nav flex-column'>")
| replace("</ul>", "</nav>")
| replace("<a", "<a class='nav-link' ")%}

3. At this point the variable tocNav contains the right HTML structure, so the next step is to display it:

<div id="local-toc-nav" class="sticky-top">
{{tocNav}}
</div>

4. The last step is to include the localtoc.html file inside the layout.html file:

{%- include "localtoc.html" %}

Copy Button in Code Blocks

In our user guide we wanted to have the copy button next to each code block but without creating or using a Sphinx plugin, because we needed to have more control around this functionality. We decided to do it via JavaScript:

  1. First of all, we created a neteye.js file and we linked it to the layout.html file by extending the block script:
{%- block scripts %}
<script src="{{pathto('_static/neteye.js', 1)|e}}" async></script>
{{ super() }}
{%- endblock %}

2. Then in the JS file, we injected the necessary HTML for the button into each code block:

var codeHighlightNodes = $('.document .highlight');
if (codeHighlightNodes.length === 0) {
    return;
}

codeHighlightNodes.prepend('<div class="copy"></div>');

3. In the CSS file we added the style, for example we added the copy icon like this:

.document .highlight .copy {
    background-image: url('./icons/copy.svg');
    border-left: solid 1px #f2f2f2;
}

4. And finally in the JS file we create the click event:

function copyToClipboard(textToCopy) {
    document.addEventListener('copy', function(e) {
        e.clipboardData.setData('text/plain', textToCopy);
        e.preventDefault();
    }, true);

    document.execCommand('copy');
}
    
/* Copy */
$('.document .highlight .copy').click((e) => {
    var el = $(e.target).parent().find('pre');
    if (el.length === 0) {
        return;
    }

    var textToCopy = el.text();
    copyToClipboard(textToCopy);
});

Responsive Layout

Last but not least, we want to cover layout responsiveness in this article.
It’s very important to build a responsive layout that fits the large variety of device screen sizes out there.

Fortunately, Bootstrap helps us a lot here, for example with a powerful grid system and with some display helpers.

Most of the time it’s enough to put the various responsive classes into the HTML code and write some CSS. For this purpose we suggest keeping in mind different screen sizes during the development of the HTML to avoid having to adapt it later, where you risking having to rewrite your code.

From our experience, we have found that Sphinx is a truly powerful and flexible tool. It’s suitable both for creating basic documentation thanks to the presence of many themes already available, and for creating a more advanced product thanks to the possibility of easily extending and customizing it.

We hope this article will help you create spectacular documentation, so give it a try yourself!

Mattia Codato

Mattia Codato

Author

Mattia Codato

Leave a Reply

Your email address will not be published. Required fields are marked *

Archive