Part 2: Advanced Configuration of Sphinx

⏰ Previous Post

If you’re just tuning in, I would highly recommend you go and checkout Part 1 of this series Part 1: Creating a Blog on Sphinx.

In the last post we installed and setup Sphinx, ablog, and Pydata Sphinx Theme, setup a basic Pydata Sphinx Theme configuration, created a blog post using reStructuredText and created a static page.

If you just want a working version, you can fork the template repo I have on my GitHub page, this will allow you to dive right into blogging without messing around with internals! 🙌

🙋🏻‍♀️ What you are going to do in this tutorial

  1. Setup the advanced configuration of Sphinx/ablog.

  2. Adding custom CSS to your website.

After you complete all three parts you should end up with an end result that looks like this:


🚀 Let’s Get Started

Let’s start by adding in some custom CSS and a HTML side panel that we can use to introduce our blog. The first thing we want to do is create a new CSS file in the _static directory. This should contain the following CSS code:

/* Bio area */
div.profile-pic {
    margin-top: 1em;

div.profile-pic img {
    border-radius: 500px;
    width: 80%;
    max-width: 190px;
    margin: 0 auto;
    display: block;

.bio-info {
    margin: 1em auto;
    max-width: 220px;

.name {
    font-size: 1.5em;

.focusareas {
    font-size: .9em;
    font-weight: bold;

.whatido {
    margin-top: 1em;

/* Sidebar for posts archive / each post */
ul.ablog-archive {
    padding-left: 0px;

.bd-sidebar h2 {
    font-size: 1.4em;

.bd-sidebar ul {
    padding-left: 0;
    list-style-type: none;

.bd-sidebar li {
    padding-bottom: .5em;
} h3, h2, ul {
    padding-left: 5%;

/* In-page post lists */
ul.postlist {
    padding-left: 0;

ul.postlist > li > p:first-child {
    font-size: 1.5em;

ul.postlist li + li {
    margin-top: 2em;

ul.postlist li > p > a {
    font-style: normal;
    font-size: 1.0em;

/* Timeline CSS */
div.timeline div.card {
    border: 0px;

div.timeline div.left {
    text-align: right;
    border-right: 1px solid black;

div.timeline div.entry::after {
    width: 1em;
    height: 1em;
    background: white;
    border-radius: 50%;
    content: "";
    top: 1em;
    display: block;
    position: absolute;
    border: 1px black solid;
    z-index: 999;

div.timeline div.entry.left::after {
    right: -.5em;

div.timeline div.entry.right::after {
    left: -.5em;

We also want to create a new HTML file in the _templates directory which should include the following content:

<div class="profile-pic"><img src="{{ pathto('_static/profile.png', 1) }}" /></div>

<div class="bio-info">
    <div class="name">
        NAME (HANDLE)
    <div class="focusareas">

    <div class="whatido">
        A paragraph about myself

Once you have created these two files your folder structure should look like this:


🎨 Adding Custom CSS

Before we can start using these new files though, we need to make sure Sphinx loads our custom CSS, this can be done by adding the following line to the bottom of your file:

def setup(app):

The add_css_file function is a part of Sphinx and registers a stylesheet to include in the HTML output. When you build your website you’ll be able to see it included in the HTML source code:

<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
<link rel="stylesheet" type="text/css" href="_static/custom.css" />
<link rel="stylesheet" type="text/css" href="_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css" />
<link rel="stylesheet" type="text/css" href="_static/panels-bootstrap.5fd3999ee7762ccc51105388f4a9d115.css" />
<link rel="stylesheet" type="text/css" href="_static/panels-variables.06eb56fa6e07937060861dad626602ad.css" />

When we rebuild our website you will notice that our blog titles are now larger as is the “Recent Posts” title:


You will probably see that we have a content header which includes your “About Me” page, which we will want to remove. This can be done by opening out index.rst and adding a :hidden: directive, this will notify Sphinx of the document hierarchy, but not insert links into the document at the location of the directive:

Recent Posts

.. toctree::
   :maxdepth: 1
   :caption: Contents:

.. postlist:: 10
   :author: errbufferoverfl
   :date: %Y-%m-%d
   :format: {date} - {title}
Recent Posts

.. toctree::
    :maxdepth: 1
    :caption: Contents:


.. postlist:: 10
    :author: errbufferoverfl
    :date: %Y-%m-%d
    :format: {date} - {title}

If you want to rebuild your site, you will now see that content list at the top is now gone:


💁🏻‍♀️ Adding a HTML Panel

In the previous step we added a HTML file to the _templates directory, now we can do something with it! In our file we want to add a html_sidebars value that should be a dictionary, this is used to that map document names to template names.

In our case, ablog comes with a number of template names that can be used including:

  • postcard.html provides information regarding the current post

  • recentposts.html lists most recent five posts.

  • authors.html, languages.html, and locations.html sidebars link to author and location archive pages.

  • tagcloud.html provides a tag cloud for post tags

  • categories.html shows categories that you have created for posts

We can also include custom HTML files like our aboutme.html page.

We can set these on a per-page basis like so:

html_sidebars = {
    "index": ["aboutme.html"],
    "about": ["aboutme.html"],
    "blog": ["tagcloud.html", "archives.html"],
    "blog/**": ["postcard.html", "recentposts.html", "archives.html"],

The result being:


Now that we have a pretty great looking blog, we can start to add extra options into our file to tune and tweak the current layout.

The Pydata Sphinx theme can also be configured, most of which are managed via the html_theme_options which is a dictionary of settings, while we cover off a small subset here there are more that you can checkout on the Pandas Read the Docs site

html_theme_options = {
    # If you want to configure Twitter or Github social media buttons to show up to the right of your nav bar,
    # you can use the "github_url" and "twitter_url" options:
    "github_url": "",
    "twitter_url": "",
    # You can also change the text that is in the search bar before people click on it by setting the
    # "search_bar_text"
    "search_bar_text": "Search for treasure...",
    # By default your site will have a search bar in the nav bar, but when we include the about.html,
    # this gets removed to so you can add one to the top "navbar" instead
    "search_bar_position": "navbar",

Next up we can add some additional configuration to our to further tune ablog, while we cover off a small subset here there are more that you can checkout on the ablog Configuration Options:

# If you used the blog file path in part one you do not need to set this value, however if you change it,
# this should be set to the same value.
# blog_path = "posts"

# blog_feed_fulltext: Choose to display full text in blog feeds.
blog_feed_fulltext = True

# Glob pattern that grabs all posts so you don't need to specify which posts are blog posts in each post
# This pattern facilitates a folder structure such as posts/2020/my-awesome-post.rst
blog_post_pattern = "posts/*/*"

# post_redirect_refresh: Number of seconds (default is 5) that a redirect page waits before refreshing the page
# to redirect to the post.
post_redirect_refresh = 1

# post_auto_image: Index of the image that will be displayed in the excerpt of the post. Default is 0, meaning no
# image. Setting this to 1 will include the first image
post_auto_image = 1

# post_auto_excerpt: Number of paragraphs (default is 1) that will be displayed as an excerpt from the post. Setting
# this 0 will result in displaying no post excerpt in archive pages.
post_auto_excerpt = 3

Presuming you have used these settings your website should look something like the following image:


🎉 Great job! Now we have one more thing to do as part of setting up our site, before it’s over for you to tinker and change to your hearts content!