The Stochastic Game
Ramblings of General Geekery

Navigation menu in PieCrust

Here’s another post in the [PieCrust cookbook series][1], this time focusing on a classic web-design pattern: the navigation menu.

Menu

It’s really just a bunch of links that you can put somewhere around each page’s content, but the trick here is to tell the user which page he’s currently on. This is pretty easy to do using [Twig macros][2] and the [data exposed by PieCrust][7], assuming of course you’re using PieCrust’s [default template engine][3].

Let’s start by building a simple layout. I skipped the parts that are not relevant:

    <!doctype html>
    <html>
    <body>
        <div id="content">
            {{ content|raw }}
        </div>
        <div id="navigation">
            <ul>
                <li><a href="{{ pcurl('') }}">Home</a></li>
                <li><a href="{{ pcurl('something/foo') }}">Foo!</a></li>
                <li><a href="{{ pcurl('about') }}">About</a></li>
                <li><a href="{{ pcurl('contact') }}">Contact</a></li>
            </ul>
        </div>
    </body>
    </html>

This is a [pretty standard PieCrust layout][4], but we want the user to know which one of those 4 navigation links is the current page. Here’s a way to do it.

Step 1: Write the macro

You can write a macro that, depending on the current page’s “slug” (the page’s URL name1), prints either a link or just some text with the active class.

{% macro nav_entry(slug, current, label) %}
{% spaceless %}
{% if current == slug %}
<span class="active">{{ label }}</span>
{% else %}
<a href="{{ pcurl(slug) }}">{{ label }}</a>
{% endif %}
{% endspaceless %}
{% endmacro %}

Note the use of the [spaceless][5] tag to make sure we don’t have ugly carriage returns and spaces in our resulting markup (we want everything on one line). This is not required – it just makes the HTML prettier.

Step 2: Import the macro in the layout

You can either put the above macro in a separate file (say, _content/templates/nav_macros.html), or at the beginning of your layout file – it depends on whether you want to reuse those macros elsewhere, and whether you want to keep your layout markup cleaner.

If you put the macro in a separate file, you’ll need to import it in your layout file with Twig’s [import][6] tag:

{% import "nav_macros.html" as nav %}

Here, we imported in the nav namespace, so you can call your macro with nav.nav_entry.

If you put the macro right there in the layout file, you have to prefix the calls with _self like so: _self.nav_entry.

For more information, see the [Twig documentation on macros][2].

Step 3: Replace links with macro

Now you can replace the navigation links with calls to the macro:

    <div id="navigation">
        <ul>
            <li>{{ nav.nav_entry('', page.slug, 'Home') }}</li>
            <li>{{ nav.nav_entry('something/foo', page.slug, 'Foo!') }}</li>
            <li>{{ nav.nav_entry('about', page.slug, 'About') }}</li>
            <li>{{ nav.nav_entry('contact', page.slug, 'Contact') }}</li>
        </ul>
    </div>

Unfortunately, you need to pass page.slug to the macro every time because the macro doesn’t have access to the current context as far as I can tell.

Add a bunch of CSS styles that will handle the active class and voilà, you’ve got a nice navigation menu.

I’ve slapped together [a little demo website here][8] so you can see it in action.


  1. Strictly speaking, PieCrust’s definition of “slug” is not quite correct since it includes the root-relative folders that contain the resource, but… well… whatever. 

    [1]: https://ludovic.chabant.com/devblog/tag/%5B%27cookbook%27%2C%20%27piecrust%27%5D/ [2]: http://twig.sensiolabs.org/doc/tags/macro.html [3]: http://bolt80.com/piecrust/doc/template-engines/ [4]: http://bolt80.com/piecrust/doc/templates/ [5]: http://twig.sensiolabs.org/doc/tags/spaceless.html [6]: http://twig.sensiolabs.org/doc/tags/import.html [7]: https://ludovic.chabant.com/devblog/2011/11/22/new-piecrust-debug-window/ [8]: https://ludovic.chabant.com/devblog_demos/nav/