IPython is an awesome tool, but I've found that figuring out how to customize the look and feel of the notebook isn't easy. My goal here is to demonstrate how you can customize the look of your notebooks using a single .css
file and use that theme wherever your notebooks go.
First, to make sure that the required CSS files are created, follow the instructions here.
The interactive notebook has a CSS override file hidden inside of your IPython profile (if you don't know how to locate it, run ipython locate
) located at:
IPYTHONDIR/profile_default/static/custom/custom.css
or on unix-based systems:
~/.ipython/profile_default/static/custom/custom.css
Anything inside this file gets priority over the default IPython theme and any CSS modifications made in this tutorial are saved inside of that file.
There's a great tutorial on updating the IPython typography by here.
I used this tutorial in creating my own CSS theme with a few tweaks (mostly fonts and muting a few interface elements). Here's the code that I used for styling the typography of my own notebook:
@import url('http://fonts.googleapis.com/css?family=Crimson+Text');
@import url('http://fonts.googleapis.com/css?family=Kameron');
@import url('http://fonts.googleapis.com/css?family=Lato:200');
@import url('http://fonts.googleapis.com/css?family=Lato:300');
@import url('http://fonts.googleapis.com/css?family=Lato:400');
@import url('http://fonts.googleapis.com/css?family=Source+Code+Pro');
/* Change code font */
.CodeMirror pre {
font-family: 'Source Code Pro', Consolas, monocco, monospace;
}
div.input_area {
border-color: rgba(0,0,0,0.10);
background: rbga(0,0,0,0.5);
}
div.text_cell {
max-width: 105ex; /* instead of 100%, */
}
div.text_cell_render {
font-family: "Crimson Text";
font-size: 12pt;
line-height: 145%; /* added for some line spacing of text. */
}
div.text_cell_render h1,
div.text_cell_render h2,
div.text_cell_render h3,
div.text_cell_render h4,
div.text_cell_render h5,
div.text_cell_render h6 {
font-family: 'Kameron';
font-weight: 300;
}
div.text_cell_render h1 {
font-size: 24pt;
}
div.text_cell_render h2 {
font-size: 18pt;
}
div.text_cell_render h3 {
font-size: 14pt;
}
.rendered_html pre,
.rendered_html code {
font-size: medium;
}
.rendered_html ol {
list-style:decimal;
margin: 1em 2em;
}
Next, I wanted to flatten out the look of the interface. Knowing that IPython uses Bootstrap makes modifying things easier, and the rest can be done just using the Google Chrome developer tools.
Here are the tweaks that I thought were important for making my notebook mine:
Here's the remainder of my customization code:
.prompt.input_prompt {
color: rgba(0,0,0,0.5);
}
.cell.command_mode.selected {
border-color: rgba(0,0,0,0.1);
}
.cell.edit_mode.selected {
border-color: rgba(0,0,0,0.15);
box-shadow: 0px 0px 5px #f0f0f0;
-webkit-box-shadow: 0px 0px 5px #f0f0f0;
}
div.output_scroll {
-webkit-box-shadow: inset 0 2px 8px rgba(0,0,0,0.1);
box-shadow: inset 0 2px 8px rgba(0,0,0,0.1);
border-radious: 2px;
}
#menubar .navbar-inner {
background: #fff;
-webkit-box-shadow: none;
box-shadow: none;
border-radius: 0;
border: none;
font-family: lato;
font-weight: 400;
}
.navbar-fixed-top .navbar-inner,
.navbar-static-top .navbar-inner {
box-shadow: none;
-webkit-box-shadow: none;
border: none;
}
div#notebook_panel {
box-shadow: none;
-webkit-box-shadow: none;
border-top: none;
}
div#notebook {
border-top: 1px solid rgba(0,0,0,0.15);
}
#menubar .navbar .navbar-inner,
.toolbar-inner {
padding-left: 0;
padding-right: 0;
}
#checkpoint_status,
#autosave_status {
color: rgba(0,0,0,0.5);
}
#header {
font-family: lato;
}
#notebook_name {
font-weight: 200;
}
/*
This is a lazy fix, we *should* fix the
background for each Bootstrap button type
*/
#site * .btn {
background: #fafafa;
-webkit-box-shadow: none;
box-shadow: none;
}
In NBViewer (and notebooks) I haven't yet found an elegent way to customize CSS. There is a popular but ugly/dangerous way which involves injecting custom css into each cell. Even the solution I'm posting here I think is poor for the reasons given here; it's likely to break on future versions and is tedious to insert.
My current working solution was borrwed from Cam DP and his book Bayesian Methods for Hackers. The idea is that if there is a <style>
tag in IPython, the notebook renders it and NBViewer also renders it. I modified Cam's code to use the default IPython profile's custom.css
file.
Just run the following code snippet in the bottom of any IPython notebook:
from IPython import utils
from IPython.core.display import HTML
import os
def css_styling():
"""Load default custom.css file from ipython profile"""
base = utils.path.get_ipython_dir()
styles = "<style>\n%s\n</style>" % (open(os.path.join(base,'profile_default/static/custom/custom.css'),'r').read())
return HTML(styles)
css_styling()
There is one curious feature in the code for NBVeiwer. This code actually checks the notebook metadata (which is nice and clean) and looks for a css theme (a specified css file, but only in the servers /static/css/theme/
directory. The unfortunate part of this is that you can only use Cam's theme from Bayesian Methods or one other linear algebra notebook theme (cdp_1.css
or css_linalg.css
respectively, and you need to remove the .css
bit when specifying a theme).
Making NBConvert do what you want is one of the trickiest bits about theming IPython. Again, the above code snippet makes NBConvert do exactly what we wanted. You can also specify a custom.css
file in the directory that you export your notebooks in and that file will automatically overwrite default styles.
Creating an NBConvert theme is a little bit more involved. NBConvert uses Jinja2 for templates. I'm not going to get into creating NBConvert templates right now because I think the default battery is pretty good for most cases. Minrk has some great examples of how to create a template here. When creating a template you don't always need the config file each time, just the .tpl
file and specifying the output format.
The command needed to generate from a custom NBConvert is (from the directory containing custom_template):
ipython nbconvert --template custom_template.tpl <notebook>
followed by:
ln .ipython/profile_default/static/custom/custom.css ./custom.css
This will convert your notebook and create a symbolic link (again, unix-based only) so that your nbconverted notebooks will change when you modify your profile's custom.css
file.