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 itssidebar
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 thedirname
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 thesidebar
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.
Sidebars on Other Pages
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.