You can leverage the "wiring" of the existing table-of-contents sidebar to create a different kind of sidebar for your home page and other pages without a table of contents.

A quick look at the home page of Close to the Machine will show you what the topic of this page is referring to: a sidebar of external links which is interesting but—like the table of contents—not so important that it can't be allowed to disappear when the screen is resized.

Being relatively new to website building, here are some of the skills I acquired with this task:

  • practice with Jinja2
  • insight into the possibilities the Python filters.py file offers you
  • a little more CSS

This page demonstrates how to create a sidebar of external links with the Urubu Quickstart project. Since I provide the code for doing the sidebar whole-hog, whether you, the reader, acquire any of these skills will depend to what extend you rely on your cut-and-paste functionality. The more you play around with what I give you, the more you'll learn.

Modify Pages in _layouts

We start with changes that need to be made in three files in the _layouts directory: _base.html, home.html and index.html. The changes are documented on my First Things First page. Note that not all of these changes are strictly necessary to get the sidebar to work, but the examples below assume you have made them.

  • Add your own .css file. See Start a Separate Css. This modification affects _base.html. It also involves creating a new .css file.

  • Put the jumbotron banner on every page. The default Urubu Quickstart has a banner on only the home page. See Put the Jumbotron on Every Page. This modification will affect every one of these three files.

Modify home.html

Now on to _layouts/home.html. In the default Urubu Quickstart, this page consists solely of a single button labeled "Start." Assuming you've made the changes described in "Modify _base.html," above, here's what your entire home.html file should look like.

<!-- For index.html in top folder only. -->
{% extends "_base.html" %}

{% block home %}
{% endblock home %}

{% block body %}

    <div class="container text-center">
      <a class="btn btn-primary btn-lg" href="start.html">Start</a>
    </div>

    <div class="container" id="homePageContainer">

        <div class="row">

            {% block content %}
                <div class="col-md-7 content" role="main">
                    <main>
                        {{this.body}}
                    </main>
                </div>

            {% endblock content %}

            {% block sidebar %}
                {% if this.sidebar is defined %}
                    {% include 'sidebar.html' %}
                {% endif %}
            {% endblock sidebar %}

        </div>

        {% include 'footer.html' %}

    </div>
{% endblock body %}

A diff between the original version and this version of the file will reveal a number of changes.

  • The jumbotron banner has been moved. (But you already knew that, from the previous section.)

  • We've added a homePageContainer container section. This is styled in the css changes described in the next section. This container contains two sections. The first is for the body of the home page. The second section of the container references a new sidebar.html template. This will cause your make to fail until your environment includes that file.

The keyword this refers to the markdown file which is invoking the home.html Jinja template. In Urubu, that is always index.md in the root folder. The template uses the this keyword twice, one in each of the sections already discussed, and uses the keyword to access two properties--this.body and this.sidebar. We'll have a look at these properties in the next section.

Modify index.md

Open index.md--the one in the root folder, not the one in any of the subdirectories such as blog and manual. Modify it so that the whole looks like this.

---
title: Urubu Quickstart
layout: home 
content:
    - start
    - content
    - customize
    - deploy
    - more 
sidebar: somelinks
tagline:
    Set up your new Urubu project quickly
---
Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.
{.lead}

Lorem ipsum dolor sit amet. 

We've given this markdown file the two properties looked for in the home.html template file discussed in the section above, namely this.sidebar and this.body. (Actually, the markdown file already had a body property, only it was an empty string until we added the Latin text above.)

Let's focus in on the part of the home.html Jinja template that uses these properties. (We've already seen the file as a whole in the previous section.)

{% block content %}
    <div class="col-md-7 content" role="main">
        <main>
            {{this.body}}
        </main>
    </div>

{% endblock content %}

{% block sidebar %}
    {% if this.sidebar is defined %}
        {% include 'sidebar.html' %}
    {% endif %}
{% endblock sidebar %}

Because the body is no longer empty, the line that references it with {{this.body}} has something to write, even though it's in a dead language which very few people can actually read anymore. More importantly for our purposes, this.sidebar is now defined, and has a value of "somelinks." Since the property is defined, the template reads the include statement and calls the sidebar.html template.

Add sidebar.html

Create a new file sidebar.html in the _layouts directory. Its contents should look like this.

<!-- Put content--usually external links, into a sidebar on the page. -->

<div class="col-md-5 col-lg-5" role="complementary">
    <nav class="hidden-print hidden-xs hidden-sm">
        <!-- Looking for the sidebar content in the same directory as the current page. -->
        {% set target = site.reflinks[this.id | dirname + this.sidebar] %}

        <div class="sidebar">
            <h3>{{target.title}}</h3>
            <ul class="style2">
                {% for item in target.body | splitlines %}
                    <li>{{item}}</li>
                {% endfor %}
            </ul>
        </div>
    </nav>
</div>

The key line here is {% set target = site.reflinks[this.id | dirname + this.sidebar] %}. Let's break it into pieces.

  • The keyword this refers to the markdown page (i.e. the .md file) which is invoking this Jinja template. As we saw in the previous section, that markdown file is index.md, and its sidebar property is set to the string "somelinks."

  • this.id refers to the ID of the index.md markdown page. What is its ID? The Urubu documentation hints at the answer, but nowhere explicitly says it. The closest it comes is here, where it says, "all content pages and folders have a corresponding reference id: their pathname without extension." Since index.md is in the root folder of the Urubu Quickstart project, its id is "/index." The id of the index.md file in the manual directory is "/manual/index". And so on.

  • The snippet this.id | dirname takes index.md's ID and passes it through the dirname Python filter. We discuss filters below, but in this particular case, the filter receives "/index" as a parameter and returns the path's directory, i.e. the root directory "/."

  • The longer snippet this.id | dirname + this.sidebar works in the following manner. It takes the "/" string described in the previous bullet and adds it to the string "somelinks," which is the value of the sidebar property in index.md. The result is the string "/somelinks."

  • site.reflinks["/somelinks"] takes the string "/somelinks" and returns a reference to the file somelinks.md.

  • Finally, you can see that the full line of Jinja code is setting the local variable target to a reference to the file somelinks.md. This will cause your build to fail if the file isn't found. So let's turn to that in the next section.

Create somelinks.md

Create a new file called somelinks.md in the root folder with these contents.

---
title: External Links
layout: simple_page 
---
[Urubu Home](http://urubu.jandecaluwe.com)

[Urubu Quickstart Online](http://urubu-quickstart.jandecaluwe.com)

[Wikipedia's Urubu Disambiguation Page](https://en.wikipedia.org/wiki/Urubu)

["urubĂș" Definition on Wikitionary](https://en.wiktionary.org/wiki/urubĂș)

As you can see, this markdown page's body consists exclusively of a series of links. Let's revisit the most important section of the sidebar.html Jinja template given above, to see how it uses the contents of this markdown file.

<!-- Looking for the sidebar content in the same directory as the current page. -->
{% set target = site.reflinks[this.id | dirname + this.sidebar] %}

<div class="sidebar">
    <h3>{{target.title}}</h3>
    <ul class="style2">
        {% for item in target.body | splitlines %}
            <li>{{item}}</li>
        {% endfor %}
    </ul>
</div>

We've already seen how the variable target gets set to a reference to somelinks.md. The HTML then displays the title property of this markdown file with h3 styling. In this case, the title is the string "External Links." The HTML after that is for a bulleted, unordered list. The contents of each line of the list are held in the item variable. This variable is set in the following Jinja for loop: {% for item in target.body | splitlines %}.

The value of target.body is obvious: it's everything below the three dashes in the somelinks.md file. In other words, it's the sequence of links that we've already seen. splitlines is our second Python filter. What it does is take a string (that is, the markdown's body) and splits it into a list of lines. So the item variable in turn holds each link, and therefore each link gets displayed as an unordered list item.

Modify filters.py

Finally now we get to the work that taught me the most as I did all this sidebar business.

In Jinja code you call a Python filter with the pipe (|) character. Currently the Quickstart project has only one filter, dateformat. It is called one time, in _layouts/index.html, with the following line: <small>{{item.date|dateformat}}</small>.

I've already briefly mentioned the two Python filters I had to add to _layouts/filters.py. After my change the entire file looked like this.

import os

def dateformat(value, format="%d-%b-%Y"):
    return value.strftime(format)

def dirname(path):
    result = os.path.dirname(path)
    if not result.endswith('/'):
        result += '/'
    return result

def splitlines(value):
    return value.splitlines()

filters = {}
filters['dateformat'] = dateformat
filters['dirname'] = dirname
filters['splitlines'] = splitlines

The behavior of the two filters dirname and splitlines has already been explained, and I hope the power of the ability to create and use Python code in your static website generator has sunk in... If it hasn't, please reread the previous sentence, over and over, until it finally does.

Add custom.css

Somewhat anticlimactically, let's make things pretty with some styling. Create a new file called custom.css and add it to the css directory. Its contents should be the following.

/* All website changes go here, so that site.css remains unchanged from the Urubu original. */

/* Stop the table of contents from getting too narrow on smaller screens. */
.sidebar {
  width: 280px;
}

/* Keep the sidebar from getting narrow as you scroll down the page. */
@media (min-width: 992px) {
  .sidebar.affix,
  .sidebar.affix-bottom {
    width: 280px;
  }
}

/* Work involving the sidebar layout for external links. */
#homePageContainer {
    margin-top: 20px;
}
.sidebar > h3 {
    margin-top: 0px;
}

Build and Enjoy

At last you're ready to build your new page. Run make and this is what you should see.

Home Page with Sidebar
Home Page with Sidebar

I sense that, now that you know how to create a sidebar of external links on your home page, you're dying to put them all over the place.

And who can blame you? The logical place to put in this kind of functionality would be in the simple_page layout, since this layout is for pages that do not get the table of contents sidebar. (See my Urubu Layouts for information on Urubu's page layouts.)

Note, however the special case of the index layout type. If the yaml front matter does not explicitly list the pages for the index page to display, Urubu discovers them on its own. Since my implementation of the sidebar puts the sidebar contents into a markdown file, for those index pages which do not list their pages via the content keyword, the sidebar page will be listed in that directory's index page. To put all this another way, my implementation of the sidebar will not work on pages using Urubu's special blog functionality--in particular its ability to list blog entries by date.