Navigation

Navigation is the way users find and traverse the different pages available on your site. For this reason, it is imperative that navigation is accessible. By their nature, links are tab-able and all keyboard users and screen readers will read them–so, if your navigation is coded with links, a screen reader should find them.

Navigation becomes more complicated when we move to tablet and mobile views. With the general practice these days being that we condense all navigation to be hidden behind a “menu” icon, we need users to be able to understand what that menu icon’s purpose is, and what to expect upon clicking on it.

Best Practices

Horizontal, Desktop Navigation
  • Code your navigation using link tags.

  • Put your links within an unordered list structure so that a screen reader will read out how many things are in the list to give visually impaired users the most information possible about the contents of the navigation.

  • Use the semantic <nav> tag around your unordered list and links with an ARIA role='navigation' attribute.

    • Testing this navigation with Chrome Vox, we found that without an ARIA role attribute, the screen reader would not read any other aria attributes–for this reason, it seems ARIA roles are crucial for screen readers.
  • Optionally, attach an aria-label attribute to your navigation to give users of assistive technology as much information as possible (especially if there are multiple navigations on your site).

    • Perkins School for the Blind website, for example, has three sets of navigation and uses an aria-label and role='navigation' for each (the ARIA labels are: ‘Utility Navigation,’ 'Accessibility Navigation,’ and 'Main Navigation’).
1
2
3
4
5
6
7
8
9
  <nav class="primary-nav primary-nav--narrow">
    <ul>
      <li><a href="/our-work/">Our work</a></li>
      <li><a href="/articles/">Articles</a></li>
      <li><a href="/about/">About us</a></li>
      <li><a href="/events/">Events</a></li>
      <li class="primary-nav__active"><a href="/contact/">Contact<span class="visuallyhidden"> (current section)</span></a></li>
    </ul>
  </nav>
Mobile Navigation

Mobile navigation often acts in very similar ways to accordions on websites. For further information, you can also look to the accordion section.

Mobile Trigger, Hamburger Button
  • Use a button tag for your mobile navigation trigger; if you must use another HTML element, ensure that you use the ARIA attribute role='button' so screen readers will know it is clickable.
  • If your button has only an image, icon, or vague link text, put an aria-label on your trigger/hamburger. (Keep in mind that if you use an aria-label, a screen reader will only read the label and not the button text most times).
  • For example, you can have: aria-label='Mobile Navigation Trigger' or 'Open Menu.’
  • Additionally, if you really want to be specific, you can toggle your button’s text between 'Open Menu’ and 'Close Menu’ with JavaScript.
  • Add and toggle an aria-expanded attribute to your button letting the user know if the content the button controls are expanded or not.
1
2
3
4
5
6
7
  <section class="other-mobile-navigation">
    <button aria-expanded="false" class="other-trigger" aria-label="Mobile Navigation Button">
      <span class='icon-menu'></span>
      <span class="other-trigger-status screenreader-text closed">Open</span>
      Menu
    </button>
  </section>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  var $otherTrigger = $('.other-trigger'),
      $otherTriggerWord = $('.other-trigger-status'),
      $otherMenu = $('.other-primary-navigation');

  function toggleOtherTriggerClass() {
    if (!$otherTrigger.hasClass('is-active')) {
      $otherTrigger.addClass('is-active');
      $otherTrigger.attr("aria-expanded","true");
    } else {
      $otherTrigger.removeClass('is-active');
      $otherTrigger.attr("aria-expanded","false");
    }
  };

  function toggleOtherTriggerWord() {
    if ($otherTriggerWord.hasClass('closed')) {
      $otherTriggerWord.removeClass('closed');
      $otherTriggerWord.addClass('open');
      $otherTriggerWord.html('Close');
    } else {
      $otherTriggerWord.removeClass('open');
      $otherTriggerWord.addClass('closed');
      $otherTriggerWord.html('Open');
    }
  };

  function toggleOtherMenuClass() {
    if (!$otherMenu.hasClass('is-visible')) {
      $otherMenu.addClass('is-visible');
      $otherMenu.focus();
    } else {
      $otherMenu.removeClass('is-visible');
    }
  };

  function toggleOtherMenu() {
    $otherTrigger.on("click", function(e){
      toggleOtherTriggerClass();
      toggleOtherMenuClass();
      toggleOtherTriggerWord();
      $otherMenu.slideToggle("fast");
    });
  };

  function resetOtherMenu() {
    if (!$otherMenu.hasClass('is-visible')) {
      $otherMenu.removeAttr('style');
    } else {
      $otherMenu.removeClass('is-visible').removeAttr('style');
    }
  };

  function resetOtherMenuTrigger() {
    if (!$otherTrigger.hasClass('is-active')) {
      return
    } else {
      $otherTrigger.removeClass('is-active');
    }
  };

  //call this function on page load
  $(document).ready(function(){
    toggleOtherMenu();
  });
Navigation List (generally hidden)
  • This should follow all of the characteristics of the horizontal menu specified above, including that is has a semantic nav tag wrapping a list of links with a role='navigation' attribute, and uses an aria-label to describe its purpose.
  • If you have your navigation hidden on mobile views, use: {display: none;} so that screen readers can not read it (if you use {visibility: hidden;}, screen readers can still sometimes read that).
  • Additionally, you can use an ARIA aria-hidden='true' attribute to ensure screen readers will not be reading your hidden content, though this is often unnecessary if you are using {display: none;}*.
  • If using an aria-hidden attribute, you should toggle this property upon showing and hiding the navigation.
1
2
3
4
5
6
7
8
  <nav aria-label="Mobile Accessibility Navigation Dropdown" class="primary-navigation" role="navigation">
    <ul class="navigation-item-group">
      <li class="nav-item"><a href="/">Semantic HTML</a></li>
      <li class="nav-item"><a href="/aria">ARIA</a></li>
      <li class="nav-item"><a href="/font">Font &amp; Font Size</a></li>
      <li class="nav-item"><a href="/color">Contrast &amp; Color</a></li>
    </ul>
  </nav>

Live Example

Tools & Resources

Other Live Examples of Accessible Horizontal and Mobile Navigation

* For navigation that moves to mobile navigation, with perhaps a display: none; nav list, it is actually not commonly accepted to use aria-hidden attributes anymore. We asked someone in the a11y slack room, and they told us that “if you’re already [applying display: none;] to elements, you generally won’t need [aria-hidden] anyway.”