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

Let's build responsive, accessible navigation: attempts part 1

15 Dec 2020 🔖 jamstack web development
💬 EN

Table of Contents

TO DO: FIX, IN SOURCE AND HTML, THE FIX-THIS CANONICALS, BEFORE PUBLISHING

TO DO: (intro here)


Posts In This Series


Files

See my starting source code on GitHub, or read about what’s in it

As we last left it, irenders a page that looks like this:

Screenshot of resulting web page

Today I’ll be trying to build out a perfect navbar by editing:

  1. the HTML in /src/_incoudes/layouts/nav.liquid
  2. the JavaScript in /static/js/main.js
  3. the SCSS files that start with underscores /src/_includes/scss/ (main.scss and subfolders’ index.scss files are just a bunch of import statements that let me break my codebase up into interesting files starting with underscores).
.
├── src
│   ├── _data
│   ├── _includes
│   │   ├── components
│   │   │   └── (various partial template includes)
│   │   ├── layouts
│   │   │   ├── nav.liquid
│   │   │   └── (various normal template includes)
│   │   └── scss
│   │       ├── abstracts
│   │       │   ├── _variables.scss
│   │       │   └── index.scss
│   │       ├── base
│   │       │   ├── _grd.scss
│   │       │   ├── _helpers.scss
│   │       │   └── index.scss
│   │       ├── layouts
│   │       │   ├── _nav.scss
│   │       │   └── index.scss
│   │       └── main.scss
│   ├── css
│   │   └── (css-rendering template)
│   ├── filters
│   │   └── (various template-helper function definitions)
│   ├── static
│   │   └── js
│   │       └── main.js
│   └── (page-rendering template)
└── (gitignore, package, etc)

Contain yourself – you’re bleeding!

The first thing I’m going to do is put a little structure into the HTML rendered by nav.liquid.

I’m starting from a pretty lightweight codebase:

<header id="masthead">
  Masthead goes here
</header>

I’m happy with my decision to use an HTML header tag at this outermost level. I like the concept of <body><header></header><main></main><footer></footer></body> for a web page’s structure.

Having first learned about 2-D layout in a print journalism class, I’m fond of Stackbit’s choice to name it “masthead” in their Exto theme, so I’ll do the same.

As much as I wish I could remember to design “mobile first,” my broadsheet-trained brain always imagines a wide desktop area “above the fold” first.

I decided that “desktop mode” layouts I admired broke down into 3 styles:

Masthead background Masthead text Main-body text Examples, as of late 2020
To the screen’s edge To the screen’s edge Fixed max width Stackrole Foundation theme, Katie Kodes
To the screen’s edge Fixed max width Fixed max width Stackbit Starter theme, Stackbit Ampersand theme, Stackbit Exto theme (it’s subtle – just a bottom-border line)
Fixed max width Fixed max width Fixed max width Stackbit DIY theme, The Minimalists

I couldn’t decide what I really liked, so I polled the Party Corgi Discord.

Brittney Postma pointed out that letting sections within the main body of a web page sporadically go “full bleed” to the screen’s edge for emphasis as seen this post by Josh Comeau can look awkward without at least letting the masthead’s looks a bit silly without a header that at least letting the masthead’s background stretch to the edge of large screens, so I should stay away from approach #3 if I thought I might want to have such elements in my down-page designs. That said, I noticed that Bandcamp makes it work.

It feels easier to be aesthetically pleasing with ample use of whitespace, so I’m going to play with option #3.

Two common CSS class name to assign HTML block elements meant to constrain width, particularly in the context of a cohesive look-and-feel for “max page width,” are container and inner.

There’s something that’s just not clicking for me about either of those two names.

The word “contained,” though – that’s feeling right.

I’m going to edit nav.liquid to read like this:

<header id="masthead">
  <div class="contained contained--lg">
    Masthead goes here
  </div>{%- comment -%}End div.contained{%- endcomment -%}
</header>

(I like what Stackbit did in a lot of their themes with the contained class taking care of one thing and a secondary contained--SizeAbbreviation class taking care of the actual number of pixels for max-width.)

I sprinkled some SCSS into my _*.scss files as follows:

$contained-padding-lr: 1.125rem;
.contained {
  margin-left: auto; // Center "contained" (non-full-bleed) content within its space
  margin-right: auto; // Center "contained" (non-full-bleed) content within its space
  max-width: 100%; // Make sure "contained" (non-full-bleed) content can't overflow its space left-right
  padding-left: $contained-padding-lr; // Bring "contained" (non-full-bleed) content in from the edges a bit, for looks
  padding-right: $contained-padding-lr; // Bring "contained" (non-full-bleed) content in from the edges a bit, for looks
}
$width-xs: 360px !default;
$width-sm: 460px !default;
$width-md: 740px !default;
$width-lg: 1140px !default;
$width-xl: 1400px !default;
.contained--lg {
  max-width: $width-lg; // If a flexi-content section has this class too, give it a pixel-based max-width
}

Here’s what my page looks like when I only assign the contained class to my div:

Screenshot

And here it is when I also assign it contained--lg:

Screenshot

I can reuse the concept of contained and contained--lg all up and down a web page, not just in the masthead. For now, I’ll only do it in the masthead so that I can see the impact I’m making.


Preventative daydreaming

Before I go any further, I should think through some issues that might come up as I try to implement a navbar.

Quality control

Everything should navigate smoothly and present sensible visual and audio (screen reader) UI whether navigating by keyboard, mouse, or touchscreen, and whether in “big screen” or “small screen” presentation.

Small screens

What should my masthead look like on a “small” screen? I’m going to try to keep things simple:

  • Leave the branding logo at the top left.
  • If the menu items will be hideable, put the phrase Menu ☰ at the top right as a control for hiding and un-hiding.
  • Let each menu item take up the full screen width.
  • If the menu has 2 levels, make it obvious which level is which, and whether the label for sub-menus is actually supposed to do anything when you click it.

Screenshot

You’re so wordy, Katie

On a wide screen, I know that I like the look of a classic “branding logo at left, nav items at right” split to a masthead.

The problem is … what do “left” and “right” mean when a content author overeagerly edits the navigation options?

Stackbit’s Exto theme looks pretty good as demonstrated:

Screenshot

But duplicating one of the items excessively with Firefox’s Developer Console (to imitate an overeager web site author), it turns out I can make the content simply flow off the page, without any scrollbars in sight to help me find it.

Screenshot

In fact, I failed to follow Stéphanie Walter’s points from Designing Adaptive Components, Beyond Responsive Breakpoints when I added “Store” to my own site – here’s one of the few magic screen-widths where it wraps awkwardly below my logo:

Screenshot

One idea is to go with a 1-row, 2-column layout for the masthead, vertically centering the contents of each cell within the masthead.

It’s not ideal, but I think it seems functional.

Screenshot

And touchy, too

Over at The Minimalists, which I found through Adam Silver’s Designing a responsive menu without a hamburger, “About” is both the header for a hover drop-down and a link to a new page.

That makes it very difficult to access the secondary menu items from a large touchscreen monitor.

In fact, it looks like they even use hover for small screens, which are usually touchscreens, so there’s a pretty noticeable amount of their navigation menu I can’t use from my phone.

Particularly if a top-level menu item can serve as a link and as a UI control at the same time, it needs to have intuitive click/keyboard options for the UI controls.

Last but not least

When making a hovering dropdown 2nd level to a navigation menu, it’s often nice to set a generous minimum width for the 2nd-level content so that people don’t have trouble moving their cursor to it or fail to notice it.

You can see here that the drop-down under “Teach Yourself” on my own site is wider than the phrase “Teach Yourself”.

Screenshot

But what if a content author does awkward things with a drop-down menu, like put it as the last item on the right, or do strange things with the amount of text?

Screenshot

I don’t have a good answer for this. I’m not enough of a CSS whiz to really get my mind around how to account for the complexities of absolute positioning and awkward content in a way that looks good.


Possible HTML

Having taken apart a lot of other navbars, here’s one structure for my HTML that I think might work.

TBD if it makes more sense to have #topnav or .topnav__body get the nav tag – I’ve seen it both ways (the latter in Stackbit’s Exto theme).

  • header#masthead
    • div.contained
      • nav#topnav.topnav.grdGrid.-middle.-between
        • div.topnav__header.grdCell
          • div.grdGrid.-middle.-between
            • a#brandingLogo.topnav__header__actionable.grdCell
            • button.topnav__hamburger
              • span.topnav__header__actionable.grdCell
        • div.topnav__body.grdCell.-fill
          • a#loop1.topnav__body__outeritem.topnav__body__actionable
          • div#loop2.topnav__body__outeritem.topnav__dropdown
            • button.topnav__dropdown__button
              • span.topnav__body__actionable
            • div.topnav__dropdown__items
              • a.topnav__body__actionable
              • a.topnav__body__actionable
              • a.topnav__body__actionable
          • a#loop3.topnav__body__outeritem.topnav__body__actionable
          • a#loop4.topnav__body__outeritem.topnav__body__actionable

The “loop” IDs wouldn’t actually be in my HTML output – my bulleted list in this post was just getting hard to read and I wanted to remember the level of HTML nesting at which my static site generator starts inserting actual menu items.

The grdGrid and grdCell classes (and their hyphenated followup classes) come from slightly-renamed copies of the CSS definitions in Shogo Sensui’s Grd framework.

With no styling, here’s what that looks like:

Screenshot

  • Branding logo” is an A (default inline display), and “Menu ☰” is a BUTTON (default inline-block display), so they appear on the same line.
  • Then we jump to a new line because the DIV (.topnav__header) containing those 2 elements has closed out and a new one has begun (.topnav__body), and DIVs default to block display.
  • Home” and “Page 1” are both As (default inline), so they appear on the same line. No one told them they had to have space between each other, so they don’t.
  • Dropdown A” is a BUTTON (default inline-block), but it’s in a DIV of its own, so it appears on the next line.
  • Dropdown A link 1” through “Dropdown A link 6” are As (default inline), so they all appear on the same line as each other. They’re surrounded by yet another DIV, so they get a line separate from “Dropdown A.” No space between them.
  • Page 2” and “Page 3” are both As (default inline), so they appear on the same line. All the DIVs for “Dropdown A” have been closed out by now, so we’re on to a new line.
  • Dropdown B” and its children display according to the same explanations as “Dropdown A” and its children.

CSS first steps

Block A tags

With a tiny addition to our CSS, we can give every A except “Branding logo” its own line.

There’s no need to give “Branding logo” its own line, since it will be an image whose size we can predict, we want “Menu ☰” (whose size we can approximately predict) sharing a line with it on small screens, and “Menu ☰” will be hidden on big screens.

a.k_topnav__body__actionable {
  display: block; // This makes links not bleed into each other (causing word-wrap issues) and also ensures the hover-color spreads up and down beyond the text characters themselves
}

Float level 1 items left

Now that each level 1 item is block-display (either by being a DIV or thanks to our previous transformation), we can float them left to make them appear one after the other in a line, wrapping to a new line if they run out of room.

Screenshot

.topnav__body__outeritem {
  float: left;
}
--- ---