Responsive, accessible navigation 2: 11ty
08 Feb 2021
First things first: I need some data items to be in the menu. And I need some templating to turn it into basic blocks of HTML. Let’s make a little 11ty SSG project.
Menu data
For sure, I’m going to have a file in _data
called site.js
, where I’ll define a single object with “site-wide” data such as:
title
homeTitle
(sometimes I like the title in the browser bar when you visit a page to look different on the “home” page vs. every other page)description
mainNavLinks
, which contains a list of objects representing URLs and labels for the 2-level navigation menu I’d like to have at the top of every page in my web site.
Several 11ty template files will refer to this data:
base.liquid
html_seo.liquid
nav.liquid
It looks like this:
module.exports = {
title: "Beautiful site",
homeTitle: "Beautiful site - no, really",
description: "Meta description",
mainNavLinks: [
{ url: "/", text: "Home" },
{ url: "/page1/", text: "Page 1" },
{
text: "Dropdown A",
sublinks: [
{ url: "/dal1/", text: "Dropdown A link 1" },
{ url: "/dal2/", text: "Dropdown A link 2" },
{ url: "/dal3/", text: "Dropdown A link 3" },
{ url: "/dal4/", text: "Dropdown A link 4" },
{ url: "/dal5/", text: "Dropdown A link 5" },
{ url: "/dal6/", text: "Dropdown A link 6" },
],
},
{ url: "/page2/", text: "Page 2" },
{ url: "/page3/", text: "Page 3" },
{
text: "Dropdown B",
sublinks: [
{ url: "/dbl1/", text: "Dropdown B link 1" },
{ url: "/dbl2/", text: "Dropdown B link 2" },
{ url: "/dbl3/", text: "Dropdown B link 3 supercalifragilisticexpialidocious" },
{ url: "/dbl4/", text: "Dropdown B link 4" },
],
},
],
};
Pages and HTML
It isn’t much use to have a menu if there are no pages to put it on.
I’ll create another file in _data
called flexipages.js
. Inside it is a list of objects, each object representing one “web page” I’d like to generate. Each object always has a plaintext slug
property (for helping build the URL its data flow into), a plaintext title
property, and a sections
property holding a list of objects, each of which in turn has a sectionType
property and perhaps some other data.
I know there are a lot of other ways to structure data for URL generation in 11ty, but I’m a fan of this one because it resembles the data structure you typically end up with when you pull data in from an API-based headless CMS. I always like to be ready to swap out my tooling and try something else.
The template loop_flexi_pages.liquid
is the heart of my static page generation. In its front matter, I specify that it should “paginate” the globally available flexipages
variable into single-unit chunks, give a local loop variable name of documentData
to each chunk, and save the resulting HTML file in an output folder whose name is the value found in that chunk of data’s slug
property.
The codebase below my “front matter” in loop_flexi_pages.liquid
gets injected into the {{ content }}
placeholder of base.liquid
at build time. I don’t bother writing boilerplate <html>
tags and such in loop_flexi_pages.liquid
– instead, I delegate that to the base.liquid
template. The job of loop_flexi_pages.liquid
is to loop through the sections
property of a given flexipage and delegate rendering its contents to an appropriate template-include file, such as section_plain.liquid
.
---
layout: "layouts/base"
pagination:
alias: documentData
data: flexipages
size: 1
addAllPagesToCollections: true
# https://firxworx.com/blog/it-devops/sysadmin/using-ansibles-regex_replace-filter-to-strip-leading-and-trailing-slashes-from-strings/
permalink: /{{ documentData.slug | removeSlashes }}/index.html
---
<p>Beginning of a flexi-page (sections below)</p>
{% if documentData.sections %}
<div class="flexi-sections">
{% assign sections = documentData.sections %}
{% for section in sections %}
{% assign sectionIsValid = section | validateSection %}
{% if sectionIsValid %}
<section class="{{ section | getSectionClassText }}">
{% include 'components/sections/functions/section_component_picker' section: section %}
</section>
{% endif %}{% comment %}End "is section valid?" IF-clause{% endcomment %}
{% endfor %}{% comment %}End loop through flexi-section data{% endcomment %}
</div>
<p>End of a flexi-page (sections above)</p>
{% endif %}
At the moment, nav.liquid
is really simple:
<header id="masthead">
Masthead goes here
</header>
(Thanks to Stackbit’s Exto theme for the idea to name it “masthead.” Very sentimental, since I first learned about 2-D design in a print journalism class.)
Rendered HTML
For a “flexipage” that looks like this:
{
slug: "/",
title: "Home",
sections: [
{
sectionType: "gibberish",
text: "Hello world"
},
]
}
Based on my templating as it stands right now, the output HTML looks like this (note where Masthead goes here
and gibberish
and Hello world
appear).
<!DOCTYPE html>
<html lang="en">
<!-- Stuff here -->
<div id="site-wrap">
<header id="masthead">Masthead goes here</header>
<main id="content">
<p>Beginning of a flexi-page (sections below)</p>
<div class="flexi-sections">
<section class="section section--gibberish">
<div class="section__body">Hello world</div>
</section>
</div>
<!-- End div.flexi-sections -->
<p>End of a flexi-page (sections above)</p>
</main>
</div>
<!-- Stuff here -->
</html>
As you can see, there’s no style to it whatsoever. Just plain old Netscape-Navigator-looking Times New Roman.
CSS generation
I’ll want my web site to have a stylesheet with a URL along the lines of https://example.com/css/style.css
, but I’d like to use SASS to build it.
style.11ty.js is the heart of my CSS generation. Thanks to Adam K. Dean for the code.
This script looks for a file called /src/_includes/scss/main.scss
, parses it with Node.js’s sass
module, and saves the resulting output CSS file to a file called style.css
in a folder called css
.
(Note: My scss
folder doesn’t live in _includes
to take advantage of 11ty’s “magic” treatment of files in the _includes
folder – I just felt like _includes
was a nice place to tuck it out of the way.)
As I beautify my site, I’ll add more files and folders into /src/_includes/scss
, and actually put something more than a comment into main.scss
.
Pass-through JavaScript
It’s probably also a good idea to be able to include a bit of JavaScript from a resource like https://example.com/static/js/main.js
. I don’t have anything in mind yet (right now, the file has nothing but a comment in it), but navigation menus often use client-side JavaScript, so I like having the scaffolding in place.
I told my .eleventy.js
file to simply pass through the contents of main.js
to my built site.
.eleventy.js
My .eleventy.js
file is the main configuration file to control the way Eleventy runs. It’s a little cluttered since I’ve customized the configuration quite a bit, but every 11ty project should at least have a small one.
Git repository
Posts In This Series
- Part 1 - Responsive, accessible navigation 1: Intro
- Part 2 - This Article
- Part 3 - Responsive, accessible navigation 3: Constraining width
- Part 4 - Responsive, accessible navigation 4: Preventative troubleshooting
- Part 5 - Responsive, accessible navigation 5: HTML structure
- Part 6 - 11ty Markdown -- group pets by color