Exporting static IPython Notebooks with style


The IPython Notebook is an incredible tool that I use almost daily. Notebooks can be exported to HTML for easy sharing using nbconvert, however I’ve never been happy with the look of the exported notebooks. In the past I’ve used the nbviewer to render the .ipynb files and display notebooks online, but I’d prefer to host the notebooks myself. I set out to make some changes so I could export HTML notebooks that looked good. The results can be seen in my Introduction to Singular Value Decomposition and NYC School Data Exploration notebooks. See below for how to.

Summary (gists):

Fixing the MathJax link

At the time of this writing the link to the MathJax library used to render mathematical symbols is broken in the current IPython release. To fix this you’ll have to change the link in the mathjax.tpl file in the directory where IPython is installed (/path/to/IPython/nbconvert/templates/html/mathjax.tpl). If you’re having trouble locating the file try searching for the file name.

Change this line in mathjax.tpl:

<script src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>

To:

<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML"></script>

Adding custom styles

I wrote a CSS style sheet to make the notebook easier to read. The full sheet is available as a gist on github. I’ve broken down the changes below.

  • Add some margins to the top and bottom of the page:
    div#notebook{
    margin-top:50px;
    margin-bottom:100px;
    }
    
  • Limit the maximum width and center the notebook. The code and text cells end up being a bit off center because of the space taken up by the input prompt, I corrected this by moving the input prompt to the left.
    div.cell{
    max-width:60em;
    margin-left:auto;
    margin-right:auto;
    }
    div.input_prompt, div.output_prompt{
    margin-left:-11ex;
    }
    
  • Add a bit more space between cells.
    div.input, div.output_wrapper{
    margin-top:1em;
    margin-bottom:1em;
    }
    
  • Add a grey line to the left of text (and heading) cells. I added this bar to make it clear to the reader where text is because I think text commentary can get lost among all the code and output. The margin is decreased and padding increased so the line (which is really a left border) extends a bit above and below the text, and borders from neighboring text cells join continuously.
    div.text_cell{
    margin-top:-2px;
    margin-bottom:-2px;
    padding-top:2px;
    padding-bottom:2px;
    border-left:2px solid #505050;
    border-collapse:collapse;
    border-top:none;
    border-bottom:none;
    }
    

Changing the template itself

Along with the mathjax.tpl file in your /path/to/IPython/nbconvert/templates/html/ directory there’s a file called full.tpl which is a Jinja2 template IPython uses to define the output of the final .html notebook. I made some edits to this file and copied it into the directory where my notebooks live as web.tpl, then converted the notebooks with the this command:

ipython nbconvert notebook_name.ipynb --template web.tpl

I make the following changes to web.tpl from full.tpl (the complete template is available as a gist on github).

  • Change style sheet link. I changed the link to where I’m keeping the style sheet on my web server. For sharing notebooks locally with colleagues I have template with the CSS described above inline in the template (and I don’t make any other modifications to the template).

    Before:

    <!-- Custom stylesheet, it must be in the same directory as the html file -->
    <link rel="stylesheet" href="custom.css">
    

    After:

    <link rel="stylesheet" href="/css/ipynb.css">
    
  • Add analytics. In the <head> element I added a google analytics tag:
    <!-- Can put analytics code here, if desired -->
  • I added cell with a link back to my main site (www.frankcleary.com) at the bottom of every notebook.

    Before:

    {% block body %}
    
      <div tabindex="-1" id="notebook" class="border-box-sizing">
        <div class="container" id="notebook-container">
    {{ super() }}
        </div>
      </div>
     
    {%- endblock body %}
    

    After:

    {% block body %}
    
      <div tabindex="-1" id="notebook" class="border-box-sizing">
        <div class="container" id="notebook-container">
          {{ super() }}
          <div class="cell border-box-sizing text_cell rendered">
            <div class="prompt input_prompt">
    	</div>
    	<div class="inner_cell">
    	  <div class="text_cell_render border-box-sizing rendered_html">
    	    <h3 style="margin-top:0px">Back to main site: <a href="https://www.frankcleary.com">www.frankcleary.com</a></h3>
    	  </div>
    	</div>
          </div>
        </div>
      </div>
     
    {%- endblock body %}