Tweaking Jupyter export HTML with Templates

In a previous post, I outlined some handy hacks for converting Jupyter notebooks to oriented HTML. This addendum describes the use of Jupyter templates and CSS edits to fine-tune exported HTML.

Jupyter exports notebooks in a variety of formats. I regularly export notebooks as Markdown, HTML, and \LaTeX. When blogging I mainly use HTML and Markdown. Standard HTML output labels code blocks with numbered prompts and indents text. The following Rust hello world program illustrates this layout.

In [2]:
// This is the main function
fn main() {
    // Statements here are executed when the compiled binary is called

    // Print text to the console
    println!("Hello World!");

Hello World!

For a few simple examples numbered indented blocks are fine but for longer posts, the redundant numbering and space-wasting indents grow tiresome. I used standard HTML output for Using jodliterate and around prompt In[7]: I was irritated with Jupyter’s defaults.

Here’s something you should know about me.

I don’t like being irritated!

In addition to indented numbered prompts the standard format suffers from other boo-boos when run through Benny’s Jupyter to transmogrifier. Markdown source code like source code McCodeyFace is set off in ugly blocks and any use of \LaTeX generates hideous inline images with nontransparent backgrounds like:

\varphi (u) = \int_{\Omega} \left[ \dfrac{\|\nabla u\|^2}{2} + \lambda\dfrac{u^2}{2} - \dfrac{(u^+)^p}{p} \right] d\mu

You can hack the background color of \LaTeX by bolting in a HEX color code suffix. This blog’s background color is currently set to cfcdcd so \sqrt[n]{1+x+x^2+x^3+\dots+x^n}&bg=cfcdcd yields:


As far as I know, there is no global \LaTeX background color setting. You are forced to hard code the suffix into every expression and if you ever change your theme’s background color you have to do it all over again.

Remember that bit about how I don’t like being irritated?

Hard coding is euphemistically called a “code smell.” In this case, it’s a foul stench. This isn’t Jupyter’s fault. Jupyter sensibly uses MathJaX to render mathematics with proper scaled outline fonts, but MathJax is an external JavaScript library and does not allow cheap peons like moi to drag in alien JavaScript libraries.

Side note to Make an exception to your no JavaScript rule for MathJax. MathJax is a well established standard library and you’re being pigheaded by not allowing it.

Some of these problems can be fixed with CSS tweaks, Jupyter templates, and nb2wp edits.

Tweaking Jupyter export formats

You can adjust Jupyter export formats with templates. Templates modify how nbconvert transforms notebook JSON .ipynb files into HTML, Markdown, and other formats. Here’s a good basic introduction to Jupyter templates.

Jupyter templates are versatile and powerful but with “great formatting power comes extreme annoyance.” I don’t want to learn yet another programming language to tweak my fricking blog! Fortunately, it’s not necessary to get deep in the jinja weeds to remove a few block labels. The following template tpl file meets my meager needs.

A brain dead Jupyter template: blankfull.tpl

{% extends 'full.tpl'%}

## change the appearance of execution count
{% block in_prompt %}
{% endblock in_prompt %}

{% block prompt %}
{% endblock prompt %}

{% block output_area_prompt %}
{% endblock output_area_prompt %}

{% block output_area %}
{% endblock output_area %}

Save the preceding as UTF-8 text with extension tpl in Jupyter’s nbconvert share directory. The location of this directory will vary for different systems. On this Windows 10 machine it can be found here:


Refer to nbconvert documentation to determine where to save tpl files on your system.

Stashing a custom template in the proper location removes block prompts and indentation but it doesn’t fix CSS issues. The CSS style file distributed with nb2wp needs a few lines changed to prevent MarkdownText from getting a white background. The values after the line numbers on the left are the settings you need to give CodeyMcCodeFace text FRAGMENTS transparent backgrounds.

1077:   background-color: transparent;  background-color: #fff;
 1529:   background-color: transparent;  background-color: #f9f2f4;
10126:   background-color: transparent;  background-color: #fff;

Finally, to control annoying \LaTeX color backgrounds add another argument bg_color_hex='' to the the Python function nb2wp.

Running nb2wp with adjustments

import os
import sys

# append nb2* script directory to system path

import nb2wput as nbu
# set notebook files - the source for this blog post
nb_file_0 = r'C:\temp\nb2wp\MoreBetterBloggingWithJupyter0.ipynb'
nb_file_1 = r'C:\temp\nb2wp\MoreBetterBloggingWithJupyter1.ipynb'
# convert notebook with (nb2wp) using
# defaults for unspecified arguments
nbu.nb2wp(nb_file_0, out_dir=r'C:\temp\nb2wp\tmp',
    css_files=[r'C:\temp\nb2wp\style.css'], save_css=True, 
    remove_attrs=True, template='full', latex="wp",
    img_dir=r'C:\temp\nb2wp\tmp\img', footer=False)
Using template: full
Using CSS files ['C:\\temp\\nb2wp\\style.css']
Saving CSS to C:\temp\nb2wp\tmp\style.css
C:\temp\nb2wp\tmp\MoreBetterBloggingWithJupyter0.html: 23132 bytes written in 5.863s
# convert notebook using tweaked CSS and custom template
nbu.nb2wp(nb_file_1, out_dir=r'C:\temp\nb2wp\tmp',
    css_files=[r'C:\temp\nb2wp\my_nb2wp_style.css'], save_css=True, 
    remove_attrs=True, template='blankfull', latex="wp", bg_color_hex='cfcdcd',
    img_dir=r'C:\temp\nb2wp\tmp\img', footer=False)
Using template: blankfull
Using CSS files ['C:\\temp\\nb2wp\\my_nb2wp_style.css']
Saving CSS to C:\temp\nb2wp\tmp\style.css
C:\temp\nb2wp\tmp\MoreBetterBloggingWithJupyter1.html: 53009 bytes written in 10.377s

Leave a Reply

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

You are commenting using your 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 )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.