Possum websites, done possumly

Part 2: Site Structure

14 minute read time / 1771 words

We've got our 'Hello World', and now we want to expand our site, and make it feel much more complete. In part two, we're going to cover site structure, getting more pages ready and adding global navigation.

Recap

Part one of this guide covered the basic installation of Eleventy and getting a local dev environment up and running.

Adding more pages

First, the easy part. We can easily duplicate index.html, call it about.html, and set off on our merry way just changing the content as we see fit. But this immediately brings us to a huge maintenance issue. For every new page we add, we've got a load of duplicate code in each file.

Here's when setting up a base template comes in. In this template, we will put all of our globally used HTML structure, and then we'll configure our home page and about page to use that template to save us having duplicate code within each page's file.

I tend to use Nunjucks for my templates. It's a nice templating language, and does just about everything I need. It's easy on the eye, is easy to understand the basics, and with the help of Eleventy can be easily extended to do a bit more when we need it to.

First things first, we need to make our template file. Create a new file called base.njk in the default includes directory named _includes and add this content:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>My Sexy Eleventy Site</title>
        <meta name="viewport" content="width=device-width">
    </head>

    <body>
        <h1>{{ title }}</h1>
        {{ content | safe }}
    </body>
</html>

Now we have a simple, flexible starting point for our site that we can use as the template for every content page we add, without having the overhead of filling every page with duplicate global code.

Now we just need to make our pages use this template. By default, all Eleventy templates will let you add what is known as Frontmatter to your pages and templates.

Frontmatter is a really easy-to-understand and use way of adding metadata to (in most cases) Markdown files. In our case, Eleventy lets us use Markdown-style Frontmatter in our content pages that we're saving as Nunjucks files since that's our template language of choice here.

Frontmatter is always the first thing in the file, and is denoted by three hyphens (---) to start it, and three more to mark the end of it.

If we update our index.html file to be a Frontmatter-led content file (and rename to index.njk, we will end up with this:

---
title: Hello World!
layout: base.njk
---
<p>
    This is the page content.
</p>

When Eleventy comes to build the site, anything in the Frontmatter can be processed and used. In our base.njk template, we've defined a placeholder of {{ title }}, and one for our page content: {{ content | safe }}

More information

The official Nunjucks templating documentation details all the filters and logic available in the language out of the box.

The title placeholder will output our Frontmatter title value: 'Hello World!', and everything after the Frontmatter block is considered the 'content', and will output that into the content placeholder.

The safe filter bypasses the default practice of escaping HTML and converting special character to entities, and just outputs raw HTML. The pipe symbol is what denotes that we're using a Nunjucks filter. It means we want to use our content variable, and pass it through the safe filter. We'll cover filters more later on.

This is exactly what we want our content to do, as we're writing raw HTML into our content page files. Our site should output exactly the same as before, but now if we've got an about.njk page as well it'll inherit the layout from our base.njk template.

Directory structure

We've now got two text pages for our site (home and about us), and one base template. Here's the current directory structure of our project:

./
├── _includes/
│   └── base.njk
├── about.njk
└── index.njk

Let's start building on this. Say we're going to write some articles. You know, like I do on this site. We can do that fairly easily in Eleventy. First things first, we need to create a new folder for them. Let's call the folder posts.

Inside this folder, we need to create the default posts page, so let's make a file called index.njk. This will be our post listing page. Since the post listing page will work differently, we will need another template.

Eleventy uses template inheritance, and it's incredibly useful and powerful. Let's create the basic structure we need for this in a new file alongside our base template called posts.njk:

---
layout: base.njk
---

{{ content | safe }}

<h2>Posts</h2>

<p>
    This is where the post listings will appear.
</p>

In this template, we're setting its layout to be that of the base template, essentially 'extending' templates upwards. This template takes the base one and allows us to do additional or different things that we don't want any page using the base template to do.

To make use of this new post listing template, we'll need to edit our ./posts/index.njk file to use this new template.

---
title: Posts
layout: posts.njk
---
<p>
    This is the post listing page content!
</p>

If you head on over to the /posts/ page now, you'll see this works exactly as expected! Now here's our updated directory structure:

./
├── _includes/
│   ├── base.njk
│   └── posts.njk
├── about.njk
├── index.njk
└── posts/
    └── index.njk

Collections

In order to start making more sense of our content, we can use Eleventy's collections. This is a way we can group content into categories that we can query independently. This is how the basics of our posts section will function.

First of all, we're going to need a new article.njk layout file. Make a new file in the includes folder and add the following content into it:

---
layout: base.njk
---
{{ content | safe }}

To assign content to a category, we need to add the tags key into the Frontmatter of our pages. But first, we're going to need to create our first post page. First, create a folder inside the posts folder for the current year, and inside that folder create another for the number of the month. So, for example /posts/2024/02/.

In the month folder, create a new file with a unique filename that might reflect the title of the post we're creating. I'm going with my-first-sexy-eleventy-post.njk. Go ahead and create the file and add the relevant Frontmatter and content to it.

We're going to need to specify the layout, a title, a date, and then the category we want to put this post in. As well as this, we can add some placeholder content as well.

---
title: My First Sexy Eleventy Post
layout: article.njk
date: 2024-02-27
tags: article
---
<p>
    This is the my first ever Eleventy post.
</p>

Eleventy uses the key tags for collections. You can put a document in one or many collections. By default, all pages are put into the all collection.

If we just want to specify one collection, we can do so as in our article content page with just tags: article which will assign this content page to the article collection.

If we want to specify that this page is part of multiple collections we can specify them like this: tags: ['article', 'othercollectionname'].

That's all we need for now, and soon we'll get to making the post listings page output any file which is set to be in the article collection.

Tagging

It's also very easy to do tagging. This is useful if you're doing blog posts and want to assign posts to separate tags so that you can have category pages on your site. I do this on Shffld, where you can click on a tag for an article and see all the other posts tagged with the same value.

Confusion around tags

There is a bit of confusion here, in that very strangely, Eleventy decided that to define a collection you assign it tags, but to tag it, you assign it categories. It feels a bit like these are backwards, but maybe I've missed the point somewhere!

Let's do some basic tagging on our first post page. Open up the file, and update the Frontmatter to the following:

---
title: My First Sexy Eleventy Post
layout: article.njk
date: 2024-02-27
tags: article
categories: ['technical', 'happy']
---
<p>
    This is the my first ever Eleventy post.
</p>

This will tag the article as 'technical' and 'happy', and sit it in the 'article' collection.

Listing our posts

So now we have a post listings page with its own template, and our very first post waiting to be listed. We've assigned it to the right collection too, so now we just need to make that listing template get the right content pages and output a list of links to them.

Now we need to expand on our static post listings page and get it to automatically list out our pages marked as being articles. Open up the posts.njk file, and update it to this:

---
layout: base.njk
pagination:
  data: collections.article
  size: 6
  alias: articles
  reverse: true
---
{{ content | safe }}

<h2>Posts</h2>

<ul>
{%- for article in articles -%}
    <li class="item">
        <h3>
            <a href="{{ article.url }}">{{ article.data.title | escape }}</a>
        </h3>
    </li>
{%- endfor -%}
</ul>

Now if you visit the /posts/ page, you should see a list with a link to your article. Magnifique! If you go to the link, you'll see your post page content and title output too. Things are starting to take shape!

Reverse output

reverse: true means that posts will be output newest first. Leaving this out, or setting it to reverse: false will output the first post (i.e. the oldest!) first.

Next on the list we need to look at getting some real navigation working so we can easily get to all our pages. We also need to get pagination nav working so that when we've got lots of posts they'll be automatically split over several pages to keep them digestible, as well as getting our sitemaps ready for search engines, and looking at setting up RSS feeds too.