A few easy steps to make a navigation more accessible

A new year, a new blogging project. Torsten Landsiedel restarted the idea of blogging more regularily. But instead of one blog post per week, he reduced it to one new blog post every two weeks, as well as one comment per two weeks. As I have also participated in previous editions, I will try my best to succeed again. But as this blog is still bilingual, I will post in English in the first week and German in the second week. So I will hopefully post 26 posts, both in English and German.

Focus on Accessibility

Simon Kraft, another long time member of the German WordPress Community, also declared Janurary 2020 to the month with a focus on accessibilty and asks bloggers to focus on that topic. This is why my first two blog posts will have a focus on accessiblity. But probably also some other blog posts this year, as it is a topic I deeply care about.

Accessible navigations

In this first blog post of 2020, I want to show you, how you can easily make your websites navigation more accessible, if it currently is not. The main focus will be on visual and keyboard accessibility.

Improve the visual accessibility

The navigation is maybe the most important element of a website. Both sigthed and visually impaired users will find most sub pages using the navigation. Therefore, the navigation has to be “visually accessible”. Not only for users, but also for assistive technilogies, such as screen readers.

Don’t hide sub menus

When visitors are using a screen reader or similar assistive technology, only elemens that are “visible” will be announced. If a sub menu is hidden using a CSS rule, than those sub menus are not visible to such a visitor.

Hide them differently

For a classical horizontal top navigation, a typical CSS rule may look like this (example from the Yoko theme from Elmastudio):

#branding #mainnav ul ul {
	display: none;
	float: left;
	position: absolute;
	top: 2em;
	left: 0;
	z-index: 99999;
}
#branding #mainnav ul li:hover > ul {
	display: block;
}

The sub menu is hidden by default. On a hover, the display property is switched to block, so the sub menu becomes visible.

Instead of hiding the element, you could move it out of the viewport, so a sighted visitor cannot see it, but a screen reader can:

#branding #mainnav ul ul {
	display: block;
	opacity: 1;
}
#branding #mainnav ul li:not(.focus):not(:hover) > ul {
	position: absolute;
	left: -999em !important;
	opacity: 0 !important;
}

With this code, we show the sub menu on default. We then move the sub menu out of the viewport and set the opcacity to “invisible”, when the top level menu item is not focused and does not have the CSS class focus applied.

Provide a focus style

In order to make the navigation keyboard accessible, it’s not only imporant, that a user can access all item, it is also important to give a visual feedback on the menu item, the visitor is currently navigating to using the keybord.

Unfortunately, many themes globally remove ony outline property from any element. You can either just set the outline for the navigation item back to the default styles, or you simply use the same styles on focus as you use for the hover state. For Yoko it could look like this:

#branding #mainnav ul li a:focus,
#branding #mainnav ul li a.focus {
	background:#F0F0F0;
	color: #999;
}

We set the styles both for the focus state and CSS class, which will help us with the second step.

Improve the keyboard accessibility

In most navigations, the hover state is used to show a sub menu. When using a website with the keyboard only, this state is not triggered or active in CSS. In order to make a navigation usable using the keyboard tab key only, we use some JavaScript, which will add a CSS class focus, when/while a menu item or one of it’s sub menu items is focused:

( function() {
	var container, menu, links, i, len;
	var navigationContainerId = 'mainnav';
	var menuItemFocusedClass  = 'focus';

	container = document.getElementById( navigationContainerId );
	if ( ! container ) {
		return;
	}

	menu = container.getElementsByTagName( 'ul' )[0];

	if ( -1 === menu.className.indexOf( 'nav-menu' ) ) {
		menu.className += ' nav-menu';
	}

	// Get all the link elements within the menu.
	links = menu.getElementsByTagName( 'a' );

	// Each time a menu link is focused or blurred, toggle focus.
	for ( i = 0, len = links.length; i < len; i++ ) {
		links[i].addEventListener( 'focus', toggleFocus, true );
		links[i].addEventListener( 'blur', toggleFocus, true );
	}

	/**
	 * Sets or removes .focus class on an element.
	 */
	function toggleFocus() {
		var self = this;

		// Move up through the ancestors of the current link until we hit .nav-menu.
		while ( -1 === self.className.indexOf( 'nav-menu' ) ) {

			// On li elements toggle the class .focus.
			if ( 'li' === self.tagName.toLowerCase() ) {
				if ( -1 !== self.className.indexOf( 'focus' ) ) {
					self.className = self.className.replace( ' focus', '' );
				} else {
					self.className += ' focus';
				}
			}

			self = self.parentElement;
		}
	}
} )();

When using this snippet, you can simply set the id attribute of the navigation container and the CSS class that should be added on a focus state in lines 3 and 4. This JavaScript does not even need a library like jQuery and should work with any theme that is using the default WordPress navigations and no other JavaScript enhancements on the navigation. This JavaScript is a slighly modified version of code from the _s theme.

With this JavaScript applied, a visitor can use the keyboard to tab-navigate throught the navigation an all it’s sub menus both forward and backward.

Bonus: Improve the usabilty on tablets

Another issue with a hover state horizontal navigation is the fact, that a mobile device like a tablet does not have a hover. When a visitor would click on any top level item, it would only show the sub menu for a split second, before navigating to this top level item.

We want a top level item with a sub menu to show the sub menu on the first click and only navigate on a second click. For top level items without a sub menu, we want those item to navigate on the first click. This can be done with this additional snippet:

( function() {
	var container;
	var navigationContainerId = 'mainnav';
	var menuItemFocusedClass  = 'focus';


	container = document.getElementById( navigationContainerId );
	if ( ! container ) {
		return;
	}

	( function( container ) {
		var touchStartFn, i,
			parentLink = container.querySelectorAll( '.menu-item-has-children > a, .page_item_has_children > a' );

		if ( 'ontouchstart' in window ) {
			touchStartFn = function( e ) {
				var menuItem = this.parentNode, i;

				if ( ! menuItem.classList.contains( menuItemFocusedClass ) ) {
					e.preventDefault();
					var menuItemLength = menuItem.parentNode.children.length;
					for ( i = 0; i < menuItemLength; ++i ) {
						if ( menuItem === menuItem.parentNode.children[i] ) {
							continue;
						}
						menuItem.parentNode.children[i].classList.remove( menuItemFocusedClass );
					}
					menuItem.classList.add( menuItemFocusedClass );
				} else {
					menuItem.classList.remove( menuItemFocusedClass );
				}
			};

			var parentLinkLength = parentLink.length;
			for ( i = 0; i < parentLinkLength; ++i ) {
				parentLink[i].addEventListener( 'touchstart', touchStartFn, false );
			}
		}
	}( container ) );
} )();

This snippet will use the ontouchstart event to provide the necessary fixes for a drop down navigation on mobile devices.

Conclusing

It is not really hard to make a website’s navigation accessible. You just have to change your CSS a bit and introduce some JavaScript. This is best done in a child theme. You can also provide the fixes to the initial theme developer, so the fix can be provided to all users of the theme. For the Yoko theme in this example, I have created a small plugin, which bundles all the fixes. Just give it a try! Install the Yoko theme and try to keyboard navigate on your page using only the tab key. Then install and activate the plugin and do the same. Also test the navigation on a tablet using your browsers device emulation function.

After that, test your own theme! Is it accessible? Can you easily make it accessible by using the techniques from this blog post? In case your theme is using a “hamburger menu”, take a look on the code from the _s theme, with provides a very user-friendly and accessible way for a mobile navigation with sub menus.

3 comments » Write a comment

  1. Neat, thanks, Bernhard! I’m currently experimenting with Susty which basically wraps the navigational menu into its own page, so no hiding of elements is required at all; the menu doesn’t have to be rendered on every page load, and the menu page itself can be cached. It’s a pretty opinionated approach, of course. If I’ll use a more standard menu design on another site in the future, I’ll definitely come back to these tips here.

    • At first I thought “In it’s own page? Like in a frameset?” but no. After checking the demo page, it really means it’s own page, just with the menu as the content.

      That it a clear idea in terms of caching and having making each page as small as possible. I just don’t think that is really accessible as well. Yes, the content of that new page can be used by a screen reader user just as easily as for any sighted user, but users can no longer find all pages by skipping to the menu, which many screen reader users do.

Leave a Reply

Your email address will not be published. Required fields are marked *