Skip to main content

Moving from Jekyll to Nikola

For some time I have been looking for ways to incorporate Jupyter notebooks into my blog. I used to blog using Jekyll and while it has great support for code blocks (and otherwise being an awesome blog, it lacks native support for ipynb notebooks.

Using nbconvert to convert a notebook to markdown technically works, but the results aren't always pretty, especially if you have tables (such as pandas dataframe outputs) in your notebooks. You need to add custom CSS for to render tables nicely and I'm not so keen on doing that. One solution I came up with is to convert dataframe outputs to images, which technically works fine, but you need to do that every single time you run a df.head() command.

The next solution I looked at was Kyso, which embeds notebooks in iframes. All you need to do is add the iframe and Kyso will take care of rendering the notebook for you. This results in a better UI, but you need to upload the notebook every time you make an update, which is a little laborious and time consuming.

I then started looking for blog engines within the the Python ecosystem, and found Nikola to be a good fit for my needs. Since I mainly work on Jupyter notebooks I need an engine which has native support for converting them into static html/css which looks clean and readable. I think Nikola handles that seamlessly. Infact, this post is a notebook itself.

In this post I'm going to keep track of changes I made to the default configuration that Nikola comes with. (mainly for me to refer back to it in the future)

Setup:

I found that creating a new virtual environment for Nikola is useful, as you need to keep track of the packages and their versions while deploying the site. I mainly work in a conda environment, but I created a simple virtualenv environment for the blog.

UI

I'm a sucker for good UI. While the default styling of Nikola is okay, I prefer something much more elegant and clean. This is something where Jekyll really shines. It has a huge plethora of awesome looking themes with great support. Anyways, Roberto Alsina has ported the lanyon theme for Jekyll to Nikola. I'm using this theme with a few overrides:

  • In lanyon.css changed max-width of container so as to use more screen space.

    @media (min-width: 56em) {
    .container {
      /* max-width: 38rem; */
      max-width: 55rem;
    }
    }
    
  • In poole.css changed background color of code blocks.

    pre.code, .highlight pre {
      background: #eef;
    }
    
  • In poole.css added margins and padding to code blocks to make them more readable.

    pre {
      background-color: #f5dacd69;
      padding: 8px 12px;
      margin-top: 10px;
      margin-bottom: 10px;
    }
    pre.literal-block, pre.doctest-block, pre.math, pre.code {
      margin-left: 0;
      margin-right: 0;
    }
    
  • In poole.css made input and output prompts hidden.
    .prompt.input_prompt {
      display: none;
    }
    .prompt.output_prompt {
      display: none;
    }
    
  • Code font changed to Monaco (in poole.css)

conf.py

conf.py is a Nikola blog's main configuration file.

  • Added navigation links to NAVIGATION_LINKS variable.
  • In order to use ipynb notebooks as posts/pages, corresponding entries need to be added to the POSTS and PAGES variables.
    POSTS = (
      ("posts/*.rst", "posts", "post.tmpl"),
      ("posts/*.md", "posts", "post.tmpl"),
      ("posts/*.txt", "posts", "post.tmpl"),
      ("posts/*.html", "posts", "post.tmpl"),
      ("posts/*.ipynb", "posts", "post.tmpl"),
    )
    PAGES = (
      ("pages/*.rst", "pages", "page.tmpl"),
      ("pages/*.md", "pages", "page.tmpl"),
      ("pages/*.txt", "pages", "page.tmpl"),
      ("pages/*.html", "pages", "page.tmpl"),
      ("pages/*.ipynb", "pages", "page.tmpl"),
    )
    
  • DATE_FORMAT = 'MMM dd, YYYY'
    
  • CREATE_SINGLE_ARCHIVE = True
    
  • Nikola allows to have subsets of posts displayed on the index page instead of full posts, which is quite handy IMO
    INDEX_TEASERS = True
    
  • This is an important one. Nikola has built-in support for MathJax via the has_math tag in posts. Additionally, in order to have inline latex (with the syntax $.$) to be rendered the following code needs to be uncommented/added to conf.py.
    MATHJAX_CONFIG = """
    <script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
          inlineMath: [ ['$','$'], ["\\\(","\\\)"] ],
          displayMath: [ ['$$','$$'], ["\\\[","\\\]"] ],
          processEscapes: true
      },
      displayAlign: 'center', // Change this to 'left' if you want left-aligned equations.
      "HTML-CSS": {
          styles: {'.MathJax_Display': {"margin": 0}}
      }
    });
    </script>
    """
    

That's about it. I'm still exploring new features in Nikola and will probably keep tweaking it going forwards.