Adding style to the notebook

Teaching Numerical Methods with IPython Notebooks, SciPy 2014

Aron Ahmadia (US Army ERDC) and David Ketcheson (KAUST)

Creative Commons License
<span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">This lecture</span> by <a xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName" rel="cc:attributionURL">Aron Ahmadia and David Ketcheson</a> is licensed under a Creative Commons Attribution 4.0 International License. All code examples are also licensed under the MIT license.

You can give your notebooks a distinctive flair, and perhaps make them more useful for your students, by modifying the way they look.

The appearance of the IPython notebook is managed through Cascading Style Sheets (CSS), a widely-used web technology. It's possible to make huge modifications to the notebook with CSS; we will just cover the basics.

Applying a stylesheet

You can apply a particular style locally to all notebooks you run by adding a custom.css file to your ipython profile directory. This is fun, but not so useful for teaching, since it will only change the look of notebooks on your own machine. What's needed is a way to distribute styling information with the notebook itself, so that anyone viewing it sees it the way you intended.

We can apply a CSS style to the notebook dynamically like this:

In [3]:
from IPython.core.display import HTML
css_file = './example.css'
HTML(open(css_file, "r").read())
Out[3]:

This is slightly intrusive -- ideally, the styling would be applied without the user needing to know or do anything about it. Such a solution is in the works, but for now the code above is the best approach available. It simply injects the contents of css_file in the header of the notebook's HTML file. Any file can be used just by replacing the path stored in css_file.

Using CSS with the Notebook

Let's take a look at the contents of that CSS file, using IPython's %cat magic function:

In [ ]:
%cat 'example.css'

We can use %load and %%html to load the file into a cell and actually apply it:

In [ ]:
%load 'example.css'

There's a lot there (notice that the notebook created a scrollable section because the output was so long). Let's walk through this custom stylesheet one piece at a time.


The first section is actually pure HTML, not CSS:

<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Arvo:400,700,400italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=PT+Mono' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Shadows+Into+Light' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Philosopher:400,700,400italic,700italic' rel='stylesheet' type='text/css'>

This loads a bunch of Google fonts that we are going to use. If you use fonts that are usually installed on most OS's, you don't need a section like this.


<style>

@font-face {
    font-family: "Computer Modern";
    src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');
}

The style tag indicates that the rest of the file is CSS.


#notebook_panel { /* main background */
    background: #888;
    color: #f6f6f6;
}

The notebook's width is limited to a maximum number of pixels. Here we set the color for the area outside the notebook, on either side.


div.cell { /* set cell width to about 80 chars */
    width: 800px;
}

This sets the maximum width of notebook cells. This can be helpful to keep longer sections of text readable if the browser is maximized on a wide screen.


div #notebook { /* centre the content */
    background: #fff; /* white background for content */
    width: 1000px;
    margin: auto;
    padding-left: 1em;
}

This centers the notebook cells (roughly), sets the notebook background to white, and sets a width that is reasonable on most laptops.

#notebook li { /* More space between bullet points */
margin-top:0.8em;
}

Just what it says: more space between items in Markdown-rendered bulleted lists.


/* draw border around running cells */
div.cell.border-box-sizing.code_cell.running {
    border: 3px solid #111;
}

This very useful bit of styling causes a dark border to appear around cells that are running, until they finish. Try it out by running the cell below.

In [ ]:
import time
time.sleep(5)
/* Put a solid color box around each cell and its output, visually linking them together */
div.cell.code_cell {
    background-color: rgba(171,165,131,0.3); 
    border-radius: 10px; /* rounded borders */
    padding: 1em;
    margin-top: 1em;
}

This sets code cell backgrounds to grey (to distinguish them from text) and adds a tan-colored background grouping around the outside of the input and output for each code cell. It also adds a margin above the cell, so that consecutive code cell borders don't touch. Try it out by running the cell below.

In [ ]:
for i in range(5):
    print i

div.text_cell_render{
    font-family: 'Arvo' sans-serif;
    line-height: 130%;
    font-size: 115%;
    width:700px;
    margin-left:auto;
    margin-right:auto;
}

This sets the width, font, and size of text in Markdown cells.


/* Formatting for header cells */
.text_cell_render h1 {
    font-family: 'Philosopher', sans-serif;
    font-weight: 400;
    font-size: 64pt;
    line-height: 100%;
    color: rgb(12,85,97);
    margin-bottom: 0.1em;
    margin-top: 0.1em;
    display: block;
}

As explained earlier, Markdown supports six levels of headers. This block sets the style of text designated as header level 1 (either designated by a single hash # at the beginning of the line, or by setting the cell type to h1. The next blocks set the style of header levels h2 through h6. Since I usually use at most 3 levels of headings, h4 through h6 are actually used as shortcuts for other styling needs.

.text_cell_render h2 {
    font-family: 'Philosopher', serif;
    font-weight: 700;
    font-size: 24pt;
    line-height: 100%;
    color: rgb(171,165,131);
    margin-bottom: 0.1em;
    margin-top: 0.1em;
    display: block;
}   

.text_cell_render h3 {
    font-family: 'Philosopher', serif;
    margin-top:12px;
    margin-bottom: 3px;
    font-style: italic;
    color: rgb(95,92,72);
}

.text_cell_render h4 {
    font-family: 'Philosopher', serif;
}

.text_cell_render h5 {
    font-family: 'Alegreya Sans', sans-serif;
    font-weight: 300;
    font-size: 16pt;
    color: grey;
    font-style: italic;
    margin-bottom: .1em;
    margin-top: 0.1em;
    display: block;
}

.text_cell_render h6 {
    font-family: 'PT Mono', sans-serif;
    font-weight: 300;
    font-size: 10pt;
    color: grey;
    margin-bottom: 1px;
    margin-top: 1px;
}

Here are examples of each header level:

h1

h2

h3

h4

h5
h6

.CodeMirror{
        font-family: "PT Mono";
        font-size: 100%;
}

The notebook uses CodeMirror (a webservice) for syntax highlighting of code. This block tells CodeMirror to use the PT Mono font. Here Mono is short for monospaced, meaning that each character in this font occupies exactly the same amount of horizontal space. It's generally recommended to stick to monospaced fonts for code.

Finally, we indicate the end of the CSS style information:

</style>

More resources

If you've never used CSS, there are a number of good tutorials available on the web. Once you understand a bit of CSS, all you need to know are the names that IPython uses for the different sections of the notebook. Unfortunately, these are not well documented at the moment. The best place to start with styling your own notebooks are the many examples out there: