Introducing Project Orca

Part 3

In the initial installment, Introducing Project Orca - Part 1, I outlined the circumstances under which a static website would be the right choice, and also furnished a succinct summary of the Metalsmith static site generator. In the follow-up post, Introducing Project Orca - Part 2, I elucidated the workings of Metalsmith.

In today's entry, I aim to give you a brief initiation to the templating engine deployed in Project ORCA.

I was on the lookout for a templating engine grounded in JavaScript and favoring a Twig like syntax, and that's when I stumbled upon Nunjucks. Interestingly, both these engines share a common predecessor, Jinja, which in turn drew its inspiration from Django’s templates. (For those unfamiliar, Django is a high-level Python Web framework). Mozilla is responsible for the maintenance of Nunjucks.

Nunjucks operates as a token-based templating system, with built-in support for variables, loops, and conditionals. It extends its functionality to encompass advanced page composition with features such as block inheritance, includes, layout inheritance, custom tags, and macros. You can dive into the detailed Nunjucks documentation here.

To help you visualize, here's an example of a Nunjucks template:

<!doctype html>
<html lang="en" itemscope="" itemtype="”http://schema.org/Article”">
  <head>
    {% include "head.html" %}
  </head>

  <body id="onTop" class="{{ body_classes }} isLoading">
    {% include "browser-upgrade.html" %}

    <div class="container">
      <div class="has-columns cf">
        <section class="main">
          {% if title %}
          <h1 class="page-title">{{ title }}</h1>
          {% endif %}
          <ul class="blog-list-vertical list-unstyled">
            {% for blogpost in pagination.files %}            
            <li class="cf">
              <a href="/{{ blogpost.path | makePermalink }}">
                <div class="blog-list-vertical__img" style="background-image: url({{ blogpost.tn }})"></div>
              </a>
              <div class="blog-info">
                <a href="/{{ blogpost.path | makePermalink }}"><h2>{{ blogpost.title }}</h2></a>
                <p>
                  by {% for index, author in blogpost.blogAuthors %}{{ author.title }}{% if not loop.last %}, {% endif %}{% endfor %}
                  <br>on {{ blogpost.date | dateFilter("MMMM D, YYYY") }}
                </p>
              </div>
            </li>       
            {% endfor %}
          </ul>

          {% include "pager.html" %}

          </section>
          <aside class="sidebar">
            <h3>Blog Categories:</h3>
            {% include "categories-list.html" %}

            <h3>Tags</h3>
            {% include "tags-list.html" %}

            <h3>Featured Posts</h3>
            <ul class="blog-list-overview">
              {% for featuredBlogPost in featuredBlogPosts %}
              <li>
                <ul class="list-unstyled">
                  <li class="blog-post-title"><a href="/{{ featuredBlogPost.path }}/">{{ featuredBlogPost.title }}</a></li>
                  <li><a class="read-more-link" href="/{{ featuredBlogPost.path }}/">Read it <span>»</span></a></li>
                </ul>
              </li>
              {% endfor %}
            </ul>
          </aside>
        </div>
      </div>

      {% block footer %}
      {% include "footer.html" %}
      {% endblock %}

      <a id="toTopButton" href="#onTop"><i class="icon icon-arrow-up"></i></a>
  
      {% block body_scripts %}
      {% include "scripts.html" %}
      {% endblock %}
    </div>
  </body>
</html>

This example uses various features of Nunjucks and many act very similar to their Javascript cousins.

Variables

This example shows a variable body_classes being added to a static class isLoading.

<body id="onTop" class="{{ body_classes }} isLoading"> ... </body>

include

include imports other templates in place. This allows the use of common page elements. For example:

<head>
    {% include "head.html" %}
</head>

if

if allows conditional rendering like the example below:

{% if title %}
    <h1 class="page-title">{{ title }}</h1>
{% endif %}

for

for allows to iterate over arrays and objects. In the example below I create all list items by iterating over an object called featuredBlogPosts.

<ul class="blog-list-overview">
    {% for featuredBlogPost in featuredBlogPosts %}
    <li>
        <ul class="list-unstyled">
        <li class="blog-post-title"><a href="/{{ featuredBlogPost.path }}/">{{ featuredBlogPost.title }}</a></li>
        <li><a class="read-more-link" href="/{{ featuredBlogPost.path }}/">Read it <span>»</span></a></li>
        </ul>
    </li>
    {% endfor %}
</ul>

Filters

From the Nunjucks Docs: "Filters are essentially functions that can be applied to variables. They are called with a pipe operator (|) and can take arguments.". In the example below the path to a blogpost is transformed into a permalink with the filter function makePermalink.

<a href="/{{ blogpost.path | makePermalink }}"><h2>{{ blogpost.title }}</h2></a>

Template Inheritance

Again from the Nunjucks docs: "Template inheritance is a way to make it easy to reuse templates. When writing a template, you can define "blocks" that child templates can override.".

In the following illustration, I define a block labeled footer and subsequently include footer.html. Consequently, any template that extends from this primary template can now enhance or replace the content within this block.

{% block footer %}
    {% include "footer.html" %}
{% endblock %}

For a thorough understanding of Nunjucks, you can refer to the complete documentation here. Additionally, I'd suggest taking a look at Chris Coyier's article, Building A Static Site With Components Using Nunjucks, published on the Smashing Magazine website, for his unique perspective on how to effectively use Nunjucks.

Scroll to top