Switching to Hugo from Nikola

I have been using Nikola to build this Blog. Its a great static site build system that is based on Python. However, It has some crazy amount of dependencies (to have reasonable looking site). It uses restructured text (rst) as the primary language for content creation. Personally, I use markdown for almost every thing else - taking notes, making diary, code documentation etc. Furthermore, given Nikola tries to support almost everything in a static site builder, lately its is becoming more and more bloated.

Case in point, recently it got support for shortcodes and although that did enable me to write posts in Markdown, but it is so difficult to develop them (It does not help to have almost no documentation/guide for their development). They are heavily tied to the plugin system with light support for template based shortcodes.

I really have nothing against Nikola. However, I did not feel at home - I wanted a light system that focused on markdown and had all the flexibilities that I wanted. My research soon brought me to Hugo.

Table of Contents

Hugo - the blazing fast site generator

Hugo is a light weight, fast and modern static website engine written in go. It literally takes just milliseconds to build your entire site. For the given lightness, it is highly flexible as well. You can organize your content however you want with any URL structure, group your content using your own indexes and categories and define your own metadata in any format: YAML, TOML or JSON. I was impressed! Keep in my mind, python is still my primary language of programming for scripting and machine learning. And, I have almost no programming experience with go.

I chose YAML for all the configuration as well as metadata. In my next step to make this move, I had to choose the theme for my blog. If you have been following me, I have been using several flavors of Bootswatch themes. So my first goal was implement my heavily modified version of bootswatch theme in Hugo.

Bootswatch Theme

Developing a theme from scratch (Well, the implementation from scratch, as I all I am trying to do is mimic/improvise my current theme from Nikola) turned out to be a great adventure and learning exercise. It helped me understand the Hugo architecture in great detail. It did help to have some good documentation written for developers, not users! Although, Hugo’s documentation can surely help itself with some cleaning and some fresher looks!

Hugo uses go templates with many extra functions and set of variables provided by Hugo. I personally feel Hugo’s template-ing system to be more flexible and easier than Mako - the one used by default by Nikola.

I converted almost all of Mako theme from Nikola website to Hugo’s format and architecture with additions (copied features and code) from a nice theme called TranqilPeak. In particular, I liked their fonts, search feature for taxonomies pages. Copying these features also meant I had to learn a bit of javascript and css. You can find a working copy of my theme in the src branch of gihub repository of my blog. I plan to release this theme as a standalone theme in near future though.

Shortcodes

Given I am using a bootstrap based theme, I like having a lot of its features available to me when I am writing in Markdown. The powerful template based shortcodes in Hugo provide a great way to write custom HTML code inside markdown. I feel Hugo shortcodes are so powerful, you could develop your own grammar of markup language in it! 😋

Some of my shortcodes are basically based on bootstrap classes like panel, label, emphasis, highlighted text, and block quotes. I also liked the figure command provided by restructured text in Nikola. Luckily, same features are available in Hugo using a default shortcode called figure. Hugo also provides several other useful default shortcodes like youtube, ref/relref for referencing other posts etc.

I have also some additional shortcodes for code-blocks and math. I will be detailing about them in a bit more detail in the next section. All of my shortcodes are available with the theme in the same github repo.

Other Caveats and Fixes

While converting to Hugo was fun, there were some caveats. The issues I faced were mainly with home page, site search, and ipython notebook posts.

Home Page with Content and Post Lists

Getting home page to work was very simple. Hugo documentation page provides a very clear details about order in which various templates are looked. For home page, you will need to provide a template for index.html. Then Inside the content folder, you can put the metadata and the content for the home page in a file named _index.md.

I also added following template code in the index.html template to get list of posts with machine learning related tags:

 
{{ $.Scratch.Add "mlposts" slice }}
{{ $tags := (slice "Machine Learning" "EDA" "Kaggle" "ML" "Deep Learning" "DL" "Data Science") }}
{{ range .Site.RegularPages }}
    {{ $page := . }}
    {{ $has_common_tags := intersect $tags .Params.tags | len | lt 0 }}
    {{ if $has_common_tags }}
        {{ $.Scratch.Add "mlposts" $page }}
    {{ end }}
{{ end }}
{{ $cand := .Scratch.Get "mlposts" }}
{{ range first 10 $cand }}
    {{ .Render "li"}}
{{ end }}


Hugo has support for several output formats, including HTML and JSON. For implementing tipue search, we need to generate a JSON file with site content. This can be done by adding following to the configuration file:

 
# Output formats
outputs:
  home: [ "HTML", "JSON"]
  page: [ "HTML"]


and, using the following index.json template:

 
{{- $.Scratch.Add "index" slice -}}
{{- range where .Site.RegularPages "Type" "not in"  (slice "page" "json" "nosearch") -}}
{{- $.Scratch.Add "index" (dict "url" .Permalink "title" .Title "text" .Plain "tags" (delimit .Params.tags ", ")) -}}
{{- end -}}
{"pages": {{- $.Scratch.Get "index" | jsonify -}}}


Now, include the following css in the <head> of your pages:

 
<link href="//cdnjs.cloudflare.com/ajax/libs/Tipue-Search/5.0.0/tipuesearch.css" rel="stylesheet" type="text/css">


The following modal code is needed to display the search results, preferably at the end of the body of the HTMLpage:

 
<div id="search-resuts" class="modal fade" role="dialog" style="height: 80%;">
<div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal">×</button>
        <h4 class="modal-title">Search Results:</h4>
      </div>
      <div class="modal-body" id="tipue_search_content" style="max-height: 600px; overflow-y: auto;">
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      </div>
    </div>
</div>
</div>


Finally, the following javascript in the lower end of the body of HTML pages:

 
<script>
$(document).ready(function() {
    var url1 = "https://cdnjs.cloudflare.com/ajax/libs/Tipue-Search/5.0.0/tipuesearch_set.js";
    var url2 = "https://cdnjs.cloudflare.com/ajax/libs/Tipue-Search/5.0.0/tipuesearch.min.js";
    $.when(
        $.getScript( url1 ),
        $.getScript( url2 ),
        $.Deferred(function( deferred ){
            $( deferred.resolve );
        })
    ).done(function() {
        $('#tipue_search_input').tipuesearch({
            'mode': 'json',
            'contentLocation': '/index.json'
        });
        $('#tipue_search_input').keyup(function (e) {
            if (e.keyCode == 13) {
                $('#search-results').modal()
            }
        });
    });
});
</script>


And, of course you will need a form/input for performing the search:

 
<span class="navbar-form navbar-right">
    <input type="text" id="tipue_search_input" class="form-control" placeholder="Search">
</span>


Code Highlighting

Although, by default Hugo provides code highlighting using the pygments, I prefer to use client-side highlighting using prism.js. I also use the following plugins of prism.js for line numbers, highlighting and cleanup of white space:

Finally, I create a shortcode called code-block to add relevant classes and variables around <code> and <pre> tags so that prism could highlight code correctly.

jupyter Notebooks as Posts

One of the advantages of using Nikola is that, it provides native support for writing Blog posts in jupyter notebooks.

But, on some Google search, I found this neat solution.

In summary, the setup is very simple - Use the linked jupyter.css file in your template, then add this css file to relevant pages. I do the this based on a metadata variable notebook: true via the following template code:

 
{{ if .Params.notebook }}
    <link href="{{ $.Site.BaseURL }}css/jupyter.css" rel="stylesheet" type="text/css">
{{ end }}


Then for any jupyter notebook, convert it to basic HTML using the following command:

 
jupyter nbconvert --to html --template basic *source_file.ipynb*


Finally, create a markdown file for your post to put the contents of this HTML file. Works like charm since markdown supports including raw HTML code!

Latex Math Equations

I used katex for using math in markdown. I was having some issue with the multi-line display math equations, so I created a shortcode called tex to write HTML code explicitly so that katex could handle that easily.

I added following code in the <head> of all of posts:

 
{{ if .Params.hasMath }}
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.css"
    integrity="sha384-wITovz90syo1dJWVh32uuETPVEtGigN07tkttEqPv+uR2SE/mbQcG7ATL28aI9H0"
    crossorigin="anonymous">
{{ end }}


And the following script at the end of the <body> section:

 
{{ if .Params.hasMath }}
    <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/katex.min.js"
    integrity="sha384-/y1Nn9+QQAipbNQWU65krzJralCnuOasHncUFXGkdwntGeSvQicrYkiUBwsgUqc1" 
    crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.7.1/contrib/auto-render.min.js"
    integrity="sha256-ExtbCSBuYA7kq1Pz362ibde9nnsHYPt6JxuxYeZbU+c=" 
    crossorigin="anonymous"></script>
    <script>
        renderMathInElement(document.body,
            {
                delimiters: [
                    {left: "\\\\begin{equation*}", right: "\\\\end{equation*}", display: true},
                    {left: "$$", right: "$$", display: true},
                    {left: "\\\[", right: "\\\]", display: true},
                    {left: "$", right: "$", display: false},
                    {left: "\\\(", right: "\\\)", display: false}
                ]
            }
        );
    </script>
{{ end }}


Now, whenever, I need to add math equations in a post, enable the hasMath: true parameter in its metadata.

So there you have it. I have my Blog now up and running with Hugo. Hope I will be more active here, since it now takes only seconds to deploy once I have a post written. No excuses now! 😜

comments powered by Disqus