Grid of Widgets With Flexbox

Lately I’ve been dipping my toes into the Flexbox pool. So far, the water is fine.

I set out to solve a problem that happens when floated widgets in a grid layout are not all the same height, and they get hung up as they reflow.

What should look like this:
a 3x3 grid of floated widgets

…ends up at certain screen widths looking like this:
the layout fails at certain screen widths.

Flexbox solves this problem by stretching each widget in a row to the height of the tallest widget in that row. The results look like this:
all widgets in each row match the height of the tallest in the row

I made a flexbox widget tutorial, showing how I used the flexbox layout model to create multiple rows of side-by-side widgets.

Keyboard Accessible Drop-Down Menu

Drop-down menus can be a great way to save screen real estate, but the standard drop-down has one major problem: it’s not accessible to people who use the keyboard to navigate.

Here’s how to fix that with jQuery.

First, the basic menu HTML:

<nav id="nav-main">
  <ul>
    <li><a href="javascript:;">Rubber Boots</a></li>
    <li><a href="javascript:;">Unicycle Parts</a></li>
    <li><a href="javascript:;">Dog Costumes</a>
      <ul>
        <li><a href="javascript:;">Pirate</a></li>
        <li><a href="javascript:;">Clown</a></li>
        <li><a href="javascript:;">Shark</a></li>
      </ul>
    </li>
    <li><a href="javascript:;">Vintage Tubas</a></li>
    <li><a href="javascript:;">Magical Items</a>
      <ul>
        <li><a href="javascript:;">Cloaks</a></li>
        <li><a href="javascript:;">Wands</a></li>
        <li><a href="javascript:;">Rings</a></li>
        <li><a href="javascript:;">Glowing Orbs</a></li>
      </ul>
    </li>
    <li><a href="javascript:;">Ostrich Feathers</a></li>
  </ul>
</nav><!-- /#nav-main -->

Next, some basic CSS:

/* NAVIGATION */

/* reset global list styles */
nav ul,
nav li {
	position: static;
	left: 0;
	list-style: none;
	padding: 0;
	margin: 0;	
}

/* style main navigation */
#nav-main {
	float: right;
	background-color: #EEE;
}

#nav-main li {
	float: left;
	position: relative;	/* set positioning context for nested lists */
}

#nav-main li li {
	width: 100%;
	border-top: 1px solid;
}

#nav-main li ul {
	position: absolute;
	top: 2.25em;		/* nested list must overlap parent anchor */
	margin-left: 1.5em;
	z-index: 1000;

	width: 8em;		/* adjust as needed for width of text */	
	background-color: #EEE;
	border: 1px solid;
	border-top: none;
}

#nav-main a:link,
#nav-main a:visited {
	display: block;
	text-decoration: none;
	color: #000;
	padding: .75em;		
}

#nav-main a:hover,
#nav-main a:focus {
		background-color: #000;
		color: #FFF;
}

The nested lists are absolutely positioned, relative to their parent list items. Now we move the nested lists offscreen with a left offset.

  • A negative left offset value will send the nested lists to the left.
  • A positive left offset value would send the nested lists to the right. We don’t want to do this, as it creates a wide page with horizontal scrolling.
#nav-main li ul {
	blah blah blah
	left: -999em;	/* negative value sets nested list off the left side of the screen */
}

We bring the nested lists back into place by changing their left offset to auto when the parent list item is hovered or has focus:

#nav-main li:hover ul,
#nav-main li:focus ul {
	left: auto;
}

The Problem

However, this presents an accessibility problem. The browser knows when a list items is hovered, but list items are not natively focusable.

As this example shows, tabbing through the links by keyboard does not offer a good user experience.

The Solution

A little jQuery provides an elegant solution by adding a class name to the nested list when the parent li’s anchor has focus.

Add the script to your HTML:

<!-- load jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<!-- add class="focused" to submenu list when parent anchor has focus -->
<script>
// parent
$(function() {
  $('li a').focus( function () {
    $(this).siblings('li ul').addClass('focused');
  }).blur(function(){
    $(this).siblings('li ul').removeClass('focused');
  });
// submenu
  $('li a').focus( function () {
    $(this).parents('li ul').addClass('focused');
  }).blur(function(){
    $(this).parents('li ul').removeClass('focused');
  });
});
</script>

… and then change your li:focus selector from this:

#nav-main li:hover ul,
#nav-main li:focus ul {
	left: auto;
}

… to this:

#nav-main li:hover ul,
#nav-main li ul.focused {
	left: auto;
}

Now we have keyboard accessible drop-down navigation. Tab through the links to see it work.

WordCamp Inspired Me

Earlier this month (November 2017) I attended and presented at WordCamp Seattle.

There were several great sessions on accessibility.

Annmarie suggested that we meet regularly and work together on learning more about building accessible web sites. She also suggested that we blog about our experience as we go. So, here I am.

I also got really inspired to learn how to use the calc() function, Flexbox, and CSS Grid, so there will likely be more about that soon.

This will also be a good place to post tutorials about other web development and design topics I’m exploring, resources for my students, etc. etc.