Snowfalling 11ty

This content is vastly out of date. 11ty 2.0, WebC, and the move from Nunjucks to Liquid have made some of these techniques excessive or questionable. If you're using WebC you've got asset buckets available to you and this issue is mostly moot. Otherwise, if we're avoiding future use of Nunjucks then you should be able to translate most any operation in Nunjucks here to Liquid (Hilariously, I wanted to write this blog post for years and a few months after I write it the very next 11ty release renders it mostly moot.)

"Snowfalling" is a somewhat niche term for enabling the ability to build uniquely customized story experiences. Specifically to emulate the New York Times' "Snow Fall: The Avalanche at Tunnel Creek", a 2012 multipart multimedia-rich feature story[1] that won a Peabody Award and generally blew the minds of a large portion of the journalism industry. Hence, we now have the term "snowfalling" since a whole industry tried to emulate what the New York Times did.

Generally speaking the basic (and unsurprising) features you need to be successful doing this are unique markup, unique CSS, and unique JavaScript expressed in such a way as to both minimize the amount of additional work required to construct content of this type and to ensure maintenance is painless and future-proof as possible. (I cannot express this enough, success requires ease.) Crucially, this has to occur alongside the more traditional writing/reading experience and the much more predictably templatized workflow in some form. I've done this a few times with varying success and I've found 11ty (1.0, at the time of this post) to be a solid contender for this kind of work. If you'd like to give it a try you technically have multiple options, but they're not particularly equal.

The smart way: Nunjucks templates

There are probably other similar ways of doing this, but this is the easiest and most comprehensive I'm aware of. The general idea is using 11ty's layouts and Nunjucks' various templating features to construct a series of general templates and a collection of unique templates that include their appropriate (compiled or otherwise[2]) assets and can easily be applied via Markdown with little effort.

I'll note I'm using this technique for the Minecraft in 360 Degrees post to load the required JavaScript and CSS to generate the VR embed. Additionally, I'm using some specific Nunjucks features elsewhere in my Markdown-based posts. To do that I have to define Nunjucks as my Markdown template engine. For the purposes of this section I'm going to assume you'll do the same (this can cause some additional issues in various places[3]).

To define Nunjucks as your Markdown engine, in your .eleventy.js you add to your return statement, in addition to what you might have there normally:

return {
  markdownTemplateEngine: "njk",
};

Then, in my opinion, your first template should be a "base" Nunjucks template from which you'll derive almost everything else, notably using a series of Nunjucks block tags if you were so inclined. (Which you would, of course, add to your default template folder in _includes/layouts.) A very simple version could like something like this:

<!DOCTYPE html>
<html>
  <head>
    {% block head %}
      your meta, styles, etc
    {% endblock %}
  </head>
  <body>

    {% block content %}
      {{ content | safe }}
    {% endblock content %}

    {% block foot %}
      your footer content
    {% endblock %}
  </body>
</html>

The block examples here are arbitrary in almost every way. If you wanted you could do something like:

  <head>
    {% block social_meta %}
      meta here
    {% endblock %}
    {% block style %}
      css here
    {% endblock %}
    {% block arbitrary_name %}
      javascript here
    {% endblock %}
  </head>

The point of the block tags, in this case, is that in the templates that extend your base template you can override these blocks and choose to either overwrite the existing block completely or add to it while including the original content. If you need, you could also add an empty block (containing no markup) like a placeholder and overwrite that in the same way. (For instance, if some templates needed a footer or a featured image and others did not.)

Now you may want to define any default Nunjucks templates that you might use. That could include a post template, or a page template, and might look something like this:

---
layout: filename_of_base
---

{% block content %}

<article >
  <div>
    <p>
      A post template.
    </p>
  </div>
  <div>
    {{ content | safe }}
  </div>
</article>

{% endblock %}

{% block foot %}
  <p>Some footer boilerplate.</p>
{% endblock %}

In this example the front-matter line layout: filename_of_base should be the filename, without extension, of the base template. Here the "content" and "foot" block will be overwritten from the base and any other block defined in the base will pass through as written without being changed. Then, if this was a post template, in your post Markdown, in addition to any other front-matter, you would do this:

---
layout: path/to/post_layout.njk
---

And then write your content in Markdown, as normal, like this.

This gives us our more standard default experience. Now we snowfall 11ty.

Snowfalling: Mixing Nunjucks and Markdown

We want to write a uniquely composed post, with unique features. There are a few ways me might accomplish that, depending on your needs. If our base template is stacked high with block tags then we might do similar to what we did above, but in Nunjucks and not Markdown:

---
layout: filename_of_base
---

{% block scripts %}
  Some additional scripts here, replacing the original block.
{% endblock %}

{% block content %}
  New content.
  This should be where you author the post,
  not the markdown file.
{% endblock %}

{% block foot %}
{{ super() }}
  Some additional scripts here,
  but not actually replacing the original.
{% endblock %}

Notably, in this example we are arbitrarily using the super function, which renders the content of the parent block. In this case the template would render the original content then render the new content. So if you had a series of common styles or JavaScript you need included everywhere, and added via a block, you might use a call to super to prevent overwriting that content.

Snowfalling another way: Nunjucks for unique things, Markdown for everything else

Instead, you could also write a unique template per unique piece of content and write almost all your content in Nunjucks. This is probably the most flexible, but time-consuming, solution. It's also my preferred option. In this scenario you write your base templates as usual and then maintain a set of unique templates you essentially only use once.

So, in addition to a base and a post template, you might have a special-post-one template that has a special layout and includes its own set of scripts/style. You would then set this as the layout in the markdown file where you're writing "special post one", potentially only using the markdown file for the 11ty meta information (title, permalink, etc.) and writing all the content directly in the template.

The less smart way: post meta

Via this method you define your JS/style files via the post meta so you can iterate over them in the base template. Then you limit your content to your Markdown files only, which means you have to mix in HTML as you write.

So your posts might look like this:

---
title: Your regular post meta, etc
tags:
- whatever
css:
- /path/to/some/style.css
js:
- https://a-remote-resource.com/some.js
- /path/to/some/javascript.js
---

Some markdown.
<div>And some markup.</div>

And then you just loop through the css and js variables (which are arrays here) in the particular spot you feel necessary. I split these by type, you might do something slightly different like by location in the page. It could look something like this:

{% if css %}
  {% for item in css %}
    <link rel="stylesheet" href="{{item}}">
  {% endfor %}
{% endif %}

When outputting them in the template you have the option, in most cases, to either link to them or to actually inline the whole file (which can be useful for computing critical CSS, among other things).


  1. You can find a story on how it was built, originally via Mozilla's Source, over on the Wayback Machine. ↩︎

  2. Which is not really the subject of this post. If you're curious you might check out google/eleventy-high-performance-blog ↩︎

  3. You're essentially running your Markdown through an additional layer of interpretation, so sometime things can get a little funny. For example, in writing this post I discovered Nunjucks was picking up the block tags in the code examples and trying to interpret them as actual live block tags. I have to either wrap them in a raw tag, or add a templateEngineOverride: md to the front matter. ↩︎

·