Screen Reader-Accessible Dropdown Menus

I’m currently working a contract as a web designer and developer for a university, where web accessibility is vitally important for all web pages. I used to think I had a pretty good idea of how to make a web site accessible, until I started actually testing sites with a screen reader (JAWS). It’s not easy to navigate the web using your keyboard and your ears, and most web sites make the task unnecessarily complicated and frustrating.

The easiest way to make your website accessible to screen reader users is by simply following best practices for semantic markup, but even this will only take you part of the way. Markup isn’t the end of the story when it comes to accessible websites, because screen readers interpret CSS and javascript as well. For example, if you use the CSS declarations display: none or visibility: hidden, you’re hiding the content from a screen reader. If the showing or hiding of content is dependent on mouse input (like on hover), then a blind user has no way to access it.

The university I work for uses drop down menus on almost every page to display important information, but when I tested many of these pages using JAWS (the most popular screen reader), I found I couldn’t access any submenus, because they were hidden using visibility:hidden.

Accessible Dropdowns

The least you need to know: you can make drop down menus screen-reader accessible by nesting submenu unordered lists inside top-level list items, then hiding them off the screen using absolute positioning.

The whole story:

If you’re very familiar with HTML and CSS, the above was probably all the information you need to rewrite your navigation for assistive technologies. However, if you’re still stumped, let’s go over the process of making an accessible drop down menu that will be easy to use for almost any user.

Note: There is one exception to the universality of this technique: users with Internet Explorer 6 or 7 who have disabled javascript. There’s really no way to have a drop down menu work for these users, the only way to compensate for the IE’s lack of :hover pseudo-class is by using javascript.

View Demo

Step 1: Semantic HMTL

The basic setup of our drop down menu should be familiar: unordered lists nested within unordered lists. Not only is this the most semantic way to do it, but it’s the best way to present content to screen readers as well.

				<li><a href="#">Home</a></li>
				<li><a href="#">Women's</a>
						<li><a href="#">Pants</a></li>
						<li><a href="#">Blouses</a></li>
						<li><a href="#">Skirts and Dresses</a></li>
						<li><a href="#">Jackets</a></li>
				<li><a href="#">Men's</a>
						<li><a href="#">Slacks</a></li>
						<li><a href="#">Shirts</a></li>
						<li><a href="#">Ties</a></li>
						<li><a href="#">Socks</a></li>
				<li><a href="#">Kids</a>
						<li><a href="#">T-Shirts</a></li>
						<li><a href="#">Swimwear</a></li>
						<li><a href="#">Shorts</a></li>

Part 2: CSS

Pretty simple, right? The basic CSS styling is similarly simple and familiar. For now we’re going to have all the submenus showing, because we’re just adding some basic styling.

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;

body {font-family: Helvetica, Arial, sans-serif;font-size: 14px; }

.container {width: 600px; margin: 40px auto;}

nav ul {list-style: none;}

nav li {
float: left;
width: 150px;
position: relative;
text-align: center;
background: #3f4c6b;

nav li:hover {
background: #516289;

nav li a {
color: #fff;
text-decoration: none;
padding: 0.5em 0;
display: block;
height: auto;
border-bottom: 0.2em solid #293144;

nav li li {text-align: left;}

nav li li a {
background: #3f4c6b;
display: block;
padding-left: 20px; width: 130px;
border-bottom: 0.1em solid #293144;

nav li li a:hover {background: #52638c;}

All we’ve done is styled the elements, and set all the list items to float. When we add more elements to the page, we’ll need to either use the clear fix technique, or set the next element to clear:both because we can’t use the overflow: hidden trick (it would hide our submenus).

Dropdown menu in full bloom

Dropdown menu in full bloom

Note that the list items need to be set to position: relative because we’re going to be absolutely positioning the nested lists in relation to their parent items.

Hiding the Nested Lists

To hide the child lists, we just set the child <ul> elements to position: absolute and then position them to the left way off the screen. When the parent element is hovered over, we set the left value to 0.

It’s important that the top: value be exactly equal to or less than the height of the parent list item (total height, including padding and borders), or the submenu will disappear as soon as you mouse off of the parent. This might drive you crazy if you’re not careful.

nav li ul {
position: absolute;
display: block;
width: 150px;
left: -9999px;
top: auto;

nav li:hover ul {left: 0;}

Step 3: Compensating for Internet Explorer

Since IE 7 and earlier don’t supper the :hover pseudo-class, we have to compensate by using a bit of javascript. We’re going to use jQuery, since I’m always already using it. If it’s the only javascript on your page, you might want to use straight javascript. All we’re doing is adding the class “hover” to the elements that are hovered over, and then updating our CSS to support both the CSS :hover pseudo-class and the jQuery-generated class.

$("nav li").hover(function() {
}, function() {

And the complementary CSS:

nav li:hover ul, nav li.hover ul {left: 0;}

Extra Step: Making it pretty

Let’s add some nice CSS3 gradients, shall we?

nav li {
float: left;
width: 150px;
position: relative;
text-align: center;
background: #3f4c6b;
background: -moz-linear-gradient(top, #3f4c6b 0%, #323c54 99%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#3f4c6b), color-stop(99%,#323c54)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #3f4c6b 0%,#323c54 99%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #3f4c6b 0%,#323c54 99%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #3f4c6b 0%,#323c54 99%); /* IE10+ */
background: linear-gradient(top, #3f4c6b 0%,#323c54 99%); /* W3C */
mmm... CSS3

mmm... CSS3


If you have the opportunity, I strongly encourage any web developer to try out JAWS at least once, and try to see the difference between an accessible web site and a inaccessible one. Sometimes the smallest differences in coding can make all the difference when you can’t rely on visual interface.

Though JAWS may seem scary and difficult for a sighted user to try, it’s important to remember that blind users of screen readers are experts and navigating their computers by sound. The goal of accessible development is not to target your content towards assistive technology, but to present your content in a way that will be meaningful to anyone who comes across it, regardless of the medium.

View Demo

This entry was posted in HTML & CSS Tutorials. Bookmark the permalink.

5 Responses to Screen Reader-Accessible Dropdown Menus

  1. Clora says:

    I really learned about much of this, but that being said, I still assumed it turned out practical. Beautiful blog!

  2. Giesela says:

    Superb related information! I have been searching for something such as this for a time now. Many thanks!

  3. This menu is definitely an improvement over the previous menus you described, since screen readers users can now access links that were previously impossible to ever get to. However, it could be further improved by bringing the sub-menus into view on focus, not just on hover, to greatly improve accessibility to keyboard-only and other non-mouse users. It could then be further *further* improved by hiding the sub-menus in a way that does keep them hidden from screen readers, but notifies them that they are there and allows them to activate them on an as-needed basis. This keeps the screen reader user from having to hear every single sub-menu read out and allows her to get straight to the top-level menu item she wants.

    I agree that everyone should try using JAWS (or NVDA or VoiceOver) at least once! It’s very enlightening. It’s also a great idea to try navigating through your pages with only the keyboard. That’s pretty eye-opening too!

  4. admin says:

    Definitely a good point you make about responding to keyboard focus, I should look into that more. I do find however that submenus are easy to skip over when using jaws if you don’t want to hear them, because the voice announces that the element has a submenu, and users can easily skip to the next top-level navigation item by hitting the down arrow.

  5. Ramya says:

    Hi, was wondering about keyboard accessibility. Jaws readers are probably just using the keyboard for interaction and I couldn’t tab to any of the links to launch the menu. Am I missing something?

Comments are closed.