Let's build responsive, accessible navigation: 11ty baseline code
15 Dec 2020
TO DO: FIX, IN SOURCE AND HTML, THE FIX-THIS CANONICALS, BEFORE PUBLISHING
I am really struggling to build an attractive theme for a personal project with a responsive, accessible, attractive 2-level navbar.
So I’ll work in public. This particular article in this series doesn’t actually over making a navbar – it just lays out the scaffolding I’m putting into the 11ty static site generator (and building into a site / hosting with Netlify), into which I will actually start building some front-end styling.
Posts In This Series
- Part 1 - Responsive, accessible navigation 1: Intro
- Part 2 - Responsive, accessible navigation 2: 11ty
- 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
Files
.
├── src
│ ├── _data
│ │ ├── flexipages.js
│ │ └── site.js
│ ├── _includes
│ │ ├── components
│ │ │ └── sections
│ │ │ ├── functions
│ │ │ │ └── section_component_picker.liquid
│ │ │ ├── section_feature.liquid
│ │ │ └── section_plain.liquid
│ │ ├── layouts
│ │ │ ├── base.liquid
│ │ │ ├── head_seo.liquid
│ │ │ ├── html_head.liquid
│ │ │ ├── nav.liquid
│ │ │ └── scripts_end.liquid
│ │ └── scss
│ │ └── main.scss
│ ├── css
│ │ └── style.11ty.js
│ ├── filters
│ │ ├── getSectionBodyClassText.js
│ │ ├── getSectionClassText.js
│ │ ├── getSectionStyle.js
│ │ ├── removeSlashes.js
│ │ └── validateSection.js
│ ├── static
│ │ └── js
│ │ └── main.js
│ └── loop_flexi_pages.liquid
├── .eleventy.js
├── .gitignore
├── netlify.toml
└── package.json
package.json, .gitignore, & netlify.toml
The 3 Node.js module dependencies I specify in my package.json, so that executing npm i
(to install the dependencies) before executing npm run build
(to run 11ty using Node.js), are:
- @11ty/eleventy
- sass
- slugify
The Netlify CLI and Node.js put a lot of junk I don’t need tracked in source control into this folder on my computer, so I have my standard basic-11ty-project .gitignore file.
To help the Netlify CLI do its job, I have a netlify.toml file.
flexipages.js & site.js
_data
is a magic folder name in Eleventy. When I “export” nested-list-shaped data from flexipages.js
, that data is available to every page-building template in my site configuration under a variable name of flexipages
. Ditto for site.js
and the variable name site
.
In flexipages.js, I’ve defined 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’ll loop over the flexipages
data using the loop_flexi_pages.liquid
11ty template file.
In site.js, I’ve defined 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
, and finally, 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.
I’ll refer to site
data from several 11ty template “include” files, including base.liquid
, html_seo.liquid
, and most importantly to this project (but not yet in this starter), nav.liquid
.
Templates, includes & filters
I don’t know why, but I’m really fond of Shopify’s Liquid templating language. Familiarity, I suppose. That’s probably one reason I keep coming back to 11ty.
HTML generation
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, giving a local loop variable name of documentData
to each chunk, and saving the resulting output HTML file in a folder that uses the text found in the chunk’s slug
property.
I don’t bother writing boilerplate <html>
tags and such here – instead, I delegate that to the base.liquid
template-include file I’ve saved elsewhere.
The codebase below my “front matter” in loop_flexi_pages.liquid
gets injected into the {{ content }}
placeholder of base.liquid
at build time.
However, loop_flexi_pages.liquid
doesn’t actually have a lot of HTML in it. Instead, it loops over the sections
property of a given flexipage and delegates rendering its contents to a different template-include file, such as section_plain.liquid
.
base.liquid is the template-include file that delegates rendering HTML for my masthead (the content at the top of every page) to yet another template-include file, nav.liquid.
There are some other supporting template-include files and “filter” files (JavaScript code that 11ty lets you use as shortcode functions in your template files, as long as you specify that you want to do so in your .eleventy.config file), all of which help me modularize my codebase for easier maintenance in the future, but none of which merit a deep dive in this post.
- Normal include templates: html_seo.liquid, html_head.liquid, scripts_end.liquid
- Partial include templates: section_component_picker.liquid, section_feature.liquid, section_plain.liquid
- Filters: getSectionBodyClassText.js, getSectionClassText.js, getSectionStyle.js, removeSlashes.js, & validateSection.js
CSS generation
style.11ty.js is the heart of my CSS generation. Thanks to Adam K. Dean for the code.
It looks for a file called main.scss in /src/_includes/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
.
That way, https://example.com/css/style.css
as referred to in the HTML rendered by html_head.liquid actually exists.
The 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.
I’ll build out several more files and folders there, and actually put something more than a comment into main.scss, as I beautify my site.
Pass-through JavaScript
scripts_end.liquid included an HTML <script>
tag looking for https://example.com/static/js/main.js
.
Unlike /css/style.css
, I don’t need 11ty to build the contents of this file for me. I’m happy to just write them in a file called main.js.
I told my .eleventy.config file to simply pass through this JavaScript file to the built site.
At the moment, main.js
only contains a comment, but it’s good to know I have the scaffolding in place in case I need some client-side JavaScript (as is common with responsive, accessible drop-down navigation menus).
.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.
Output
Here’s a screenshot of the resulting web page, https://example.com/index.html
(a.k.a. https://example.com
):
And here’s the HTML that Eleventy built me and stored in the file /index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Begin sync CSS -->
<link rel="stylesheet" href="/css/style.css" />
<!-- End sync CSS -->
<!-- Begin pre-connect to certain 3rd-party domains like Cloudinary and Google Fonts -->
<link rel="preconnect" href="https://res.cloudinary.com" crossorigin />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!-- End pre-connect to certain 3rd-party domains like Cloudinary and Google Fonts -->
<!-- Begin some asynchronous CSS includes -->
<!-- End some asynchronous CSS includes -->
<!-- Begin no-JS fallback for async CSS includes -->
<noscript> </noscript>
<!-- End no-JS fallback for async CSS includes -->
<link
rel="icon"
href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%3E%3Ctext%20x='0'%20y='14'%3E💄%3C/text%3E%3C/svg%3E"
type="image/svg+xml"
/>
<title>Beautiful site - no, really</title>
<meta name="description" contents="Meta description" />
<meta name="generator" content="Eleventy - 11ty - https://11ty.dev" />
<link rel="canonical" href="" /><!-- TO DO: FIX THIS -->
<!-- OG cards -->
<meta property="og:title" content="Beautiful site - no, really" />
<meta property="og:description" content="Meta description" />
<meta property="og:site_name" content="Beautiful site - no, really" />
<meta property="og:locale" content="en_US" />
<meta property="og:url" content="" /><!-- TO DO: FIX THIS -->
<!-- End OG cards -->
</head>
<body>
<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>
<!-- Current page: / -->
</div>
<script src="/static/js/main.js" defer></script>
</body>
</html>