Creating accessible menus-Part 2

In my previous post I covered some of the basic steps I took to make my site navigation more accessible. In the second installment I want to finish up by taking an accessible approach to both indicating the current page and making the menu responsive.

Indicating the current page

Although not essential it’s nice to give users some type of indication of which page they are on. Unfortunately most people rely only on visual styling to achieve this, which leaves out a significant portion of their audience. I’m certainly guilty of this, in the past I’ve simply applied a class attribute of “current” to the current link and styled it accordingly. Thankfully there is a quick and easy way to indicate the current page to assistive technologies that also allows you to easily style the related link. The ARIA attribute aria-current allows us to represent the current item from a group of related elements, such as pages in a website. It’s similar to aria-selected except that aria-selected is designed to represent an element selected by the user and is more suited for widgets like tabbed panels whereas aria-current is more suited to pagination. To indicate the current page I simply needed to modify the current page’s link to this:

<a href="{{site.baseurl}}/index.html" title="articles" aria-current="page">

That’s easy enough but because I build my site with Jekyll I needed to figure out a way to apply the attribute to individual pages. My menu is so small I decided not to bother generating it dynamically so it’s hard coded into my default layout template. That means the same menu is included on every page, as the default layout template is used throughout the entire site. In order to apply the aria-current attribute to the proper link I would need some way of indicating that the page being built is the current page. For that I turned to page variables. In the front matter of each page I can simply include a variable that tells Jekyll that this is the current page. Then in my menu I can use some conditional logic to check for the variable and apply the attribute when it’s found. So in my front matter I would add a variable like this, with the value of the variable indicating the current page:

---
current: blog
---

Next I modified my menu to check for the variable and apply the attribute to the appropriate link. You might notice that the link to the blog evaluates two variables. That’s due to me also using a layout variable so that I don’t have to include a page variable in every single post. It’s a new quirk of Jekyll 3 that tripped me up initially, so I will probably blog about it later. So my menu now looks like this, note the conditional logic that evaluates the page variable:

<nav aria-label="main navigation">
<ul id="menu">
  <li><a href="{{site.baseurl}}/index.html" title="articles" {% if page.current == 'blog' or layout.current == 'blog' %}aria-current="page"{% endif %}>Blog</a></li>
  <li><a href="{{site.baseurl}}/about.html" title="learn more about me" {% if page.current == 'about' %}aria-current="page"{% endif %}>About</a></li>
  <li><a href="{{site.baseurl}}/speaking.html" title="speaking engagements" {% if page.current == 'speaking' %}aria-current="page"{% endif %}>Speaking</a></li>
  <li><a href="{{site.baseurl}}/contact.html" title="contact me" {% if page.current == 'contact' %}aria-current="page"{% endif %}>Contact</a></li>
  <li><a href="{{site.baseurl}}/tags" title="all posts" {% if page.current == 'archive' %}aria-current="page"{% endif %}>Archives</a></li>
  </ul>
</nav>

To finish the process and indicate the current page visually I added some basic attribute selectors to my CSS like this one:

nav a[aria-current] {
	color: #333;
}

Making the responsive accessible

With the basics of my menu out of the way it was time to think about how to make it responsive. Even though the menu is super small it still takes up a fair amount of room at small screen sizes, so I wanted to remove it and give the users an option of showing and hiding the menu. I considered some offscreen navigation patterns but in the end I decided to simply toggle the visibility of the menu on and off. Part of this was driven by the fact that I was basing my redesign off of the blog I designed for my Jekyll for Web Designers course. Since coding is rather tricky for me if I could reuse elements from that blog it would be a real timesaver.

In the course I had used the checkbox hack to create a toggle to show and hide the menu. I’m a huge believer in keeping things simple, since the checkbox hack only required HTML and CSS I thought it was a good choice for the course as it removes the layer of complexity that JavaScript often adds. Unfortunately there is one problem, the checkbox hack is not very accessible. So I decided to use an actual button element for the toggle rather than the checkbox. Looking around I found a few examples of accessible toggle buttons and used them as the basis for my menu.

In my menu I replaced this from the previous menu:

<input type="checkbox" id="menu-toggle">
<label for="menu-toggle" class="toggle-label"><span>menu</span></label>

With this:

<button id="toggle">Menu</button>

It’s cleaner, more semantic, and comes with keyboard accessibility already baked in.

Toggling visibility

The downside to using a button is that now I have to introduce some JavaScript in order to toggle the visibility of the menu. I’m certainly not opposed to JavaScript per se, it’s just that I like to reduce complexity wherever I can. I feel like it’s important to note that JavaScript often gets a bad rap in terms of accessibility. Adding JavaScript does not mean sacrificing accessibility. Since I’m going to use CSS to control the visibility of the menu I simply need to use JavaScript to apply a class based on the toggle button’s state. I started with this simple script:

//menu toggle for small screens
    var toggle = document.querySelector('#toggle');
    var menu = document.querySelector('#menu');

    toggle.addEventListener('click', function(){
      if (menu.classList.contains('is-active')) {
        menu.classList.remove('is-active');
      } else {
        menu.classList.add('is-active'); 
      }
    });

Then in my CSS I added styles to my small screen Media Query to initially hide the menu and then show it when the toggle button is clicked:

#menu {
	display: none;
}
#menu.is-active {
	display: block;
}

Awesome! Now I have a working menu toggle at smaller screen sizes that is by default keyboard accessible. There’s still the slight issue of relying on JavaScript for the menu’s visibility. If JavaScript is not available then there is no way to access the menu and the use of display: none means that the menu is also inaccessible to screen readers. Not very accessible and certainly goes against Progressive Enhancement, something I’m a big fan of. So I need a way to test if JavaScript is available and then apply my styles. My solution was pretty crude. In the head of my layout template I included this script:

  <script>
        (function() {
            document.documentElement.className = "js";
        }());
  </script>

All this does is apply the class of “js” to the body element, something that won’t happen if JavaScript is not available. I then modified my menu selectors to look like this:

.js #menu {
	display: none;
}
.js #menu.is-active {
	display: block;
}

Now the menu will only be hidden if JavaScript is available, otherwise users will see the menu with a big, ugly, inoperable menu button above it. Not ideal, but it provides a fallback if JavaScript is unavailable. I’m sure there are more elegant ways to do this and I’m not a fan of placing a render blocking script in the head of my document, but it allows me to progressively enhance the menu with a minimum of code.

Enhancing accessibility

Although my menu is functional and keyboard accessible it’s not as accessible as it should be. Since the menu has states that are controlled by a toggle button we should report those states to assistive user agents so that their users will know if the menu is expanded or not. Once again ARIA comes to our rescue with the aria-expanded attribute, which allows us to indicate the current state of a collapsible element. I’ll also need to indicate that the menu button controls the toggling of the menu. For that I’ll use the aria-controls attribute. With that in mind I modify my menu button to look like this:

<button id="toggle" aria-expanded="false" aria-controls="menu">Menu</button>

This tells assistive user agents that the button controls the menu and that its current state is collapsed. Since it is a toggle we will need to modify our script to change the value of the attribute as the menu is expanded and collapsed:

    //menu toggle for small screens
    var toggle = document.querySelector('#toggle');
    var menu = document.querySelector('#menu');

    toggle.addEventListener('click', function(){
      if (menu.classList.contains('is-active')) {
        this.setAttribute('aria-expanded', 'false');
        menu.classList.remove('is-active');
      } else {
        menu.classList.add('is-active'); 
        this.setAttribute('aria-expanded', 'true');
      }
    });

As an added bonus I can now change the visual state of the toggle button based on the value of the aria-expanded attribute. I added the following rule to my CSS:

.js #toggle[aria-expanded="true"]:after {
    content: " [-]";	
    }

I now have a functioning toggle button that is equally accessible visually, via the keyboard, and for screen readers. Even better the menu is accessible whether JavaScript is available or not. I also avoided potential issues with motor impairments by avoiding unnecessary off screen patterns or gesture activations. Is it incredibly basic? Yes, but it fits the aesthetics of my blog and remains true to the simple nature of the menu itself.

My final menu, toggle button and all looks like this:

<nav aria-label="main navigation">
<button id="toggle" aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu">
  <li><a href="{{site.baseurl}}/index.html" title="articles" {% if page.current == 'blog' or layout.current == 'blog' %}aria-current="page"{% endif %}>Blog</a></li>
  <li><a href="{{site.baseurl}}/about.html" title="learn more about me" {% if page.current == 'about' %}aria-current="page"{% endif %}>About</a></li>
  <li><a href="{{site.baseurl}}/speaking.html" title="speaking engagements" {% if page.current == 'speaking' %}aria-current="page"{% endif %}>Speaking</a></li>
  <li><a href="{{site.baseurl}}/contact.html" title="contact me" {% if page.current == 'contact' %}aria-current="page"{% endif %}>Contact</a></li>
  <li><a href="{{site.baseurl}}/tags" title="all posts" {% if page.current == 'archive' %}aria-current="page"{% endif %}>Archives</a></li>
  </ul>
</nav>

Final thoughts

Even though my menu is incredibly simple I hope that this series of blog posts show how much thought should be put into making your navigation accessible. Navigation is a critical and essential part of any website if it is not accessible it can severely impact the usability of your site. For more information check out the W3C’s tutorials on accessible navigation. Feel free to reach out to me on Twitter or through this blog to discuss anything covered in this post!

Read more: