Salesforce, Python, SQL, & other ways to put your data where you need it

Need event music? 🎸

Live and recorded jazz, pop, and meditative music for your virtual conference / Zoom wedding / yoga class / private party with quality sound and a smooth technical experience

Responsive, accessible navigation 2: 11ty

08 Feb 2021 🔖 jamstack web development
💬 EN

Table of Contents

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.

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.

Screenshot of resulting web page

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

See the codebase on GitHub


Posts In This Series

--- ---