Header burger menu

A burger menu with a left side panel that can hold navigation and other widgets on mobile devices.

The burger menu is a left side panel that can hold the main navigation on small screens.

Panel control

The burger menu is a complicated component, to make it accessible it's not enough to simply visually display it and make it slide, we need to make sure the whole thing is wired correctly and some user interactions should be manipulated to effectively lock focus in and out of the panel.

Here are the details you should pay attention to:

The first toggle button:

  • Opens the panel that contains the burger menu
  • Requires an internationalized aria-label that tells the user what's the purpose of the button, in this case "Open the menu panel" or something equivalent
  • Has two important attributes:
    • aria-controls="{the-panel-id}" that will associate the toggle with the panel
    • aria-expanded="true|false" this informs the state of the panel: opened or closed

The second toggle button inside of the panel:

  • Is for closing the panel and should be the first element of the panel container to receive focus when the panel is opened
  • Has the same ARIA attributes as the first button, and the aria-expanded status of both buttons should be in sync
  • Also requires an aria-label that says "Close the menu panel" or similar
  • Receives focus when the panel is opened

The panel container:

  • Should have an id that is passed to the toggle's aria-controls attribute
  • When collapsed must have the hidden attribute, this effectively removes it from the tab order and from the accessibility tree

To open the panel:

  • Set both aria-expanded of the toggle buttons to true
  • Remove the hidden attribute from the panel
  • Set the focus on the first element of the panel (which is the close button)
  • Block users from scrolling the page by adding overflow: hidden; CSS rule to the body element

To hide the panel:

  • Set aria-expanded of both toggles to false
  • Add hidden attribute to the panel
  • Set the focus back on the first toggle (the burger button) so users can continue navigating the web site
  • Remove the overflow: hidden CSS rule from the body to enable scrolling again
  • Users must be able to close the panel by pressing ESC on the keyboard
  • You should close the panel when users tab away from it (when any element that is not inside of the panel receives focus)
<header class="flix-header flix-header--unfixed">
  <div class="flix-header__inner">
    <div class="flix-header-brand flix-header-brand--square">
      <a class="flix-header-brand__link" href="/">
        <img class="flix-header-brand__img" width="36" height="36" src="/img/logos/svg/honeycomb-white.svg" alt="Honeycomb Logo"/>
      </a>
    </div>
    
    <div class="flix-header-burger-menu">
      <button type="button" aria-label="Open main site navigation" class="flix-header-burger-menu__toggle flix-btn flix-btn--link flix-btn--square flix-btn--md" aria-controls="menu-panel" aria-expanded="false"></button>
      <div id="menu-panel" class="flix-header-burger-menu__panel" hidden="">
        <button type="button" aria-label="Close main site navigation" class="flix-header-burger-menu__toggle flix-btn flix-btn--link flix-btn--square flix-btn--md" aria-controls="menu-panel" aria-expanded="false"></button>
        The burger menu content.
      </div>
      <div class="flix-header-burger-menu__overlay flix-overlay"></div>
    </div>
    
  </div>
</header>

Burger menu navigation

The burger menu navigation markup is identical to the header navigation, only changing the class names. Even the sub-menu control. You may even use the same component for both, making sure to adapt the class names and ids according to the position you're using it.

If you have the same navigation on the hamburger menu and on the header navigation, you should provide the same aria-label for both nav landmarks, this enables screen readers to only index one of them, reducing the clutter.

Also, make sure to provide different ids for the sub-menus, since you are not allowed to have multiple elements with the same id on a page.

The burger menu navigation class names are: flix-header-burger-menu then add the element class names just the same as in the header navigation.

<header class="flix-header flix-header--unfixed">
  <div class="flix-header__inner">
    <div class="flix-header-brand flix-header-brand--square">
      <a class="flix-header-brand__link" href="/">
        <img class="flix-header-brand__img" width="36" height="36" src="/img/logos/svg/honeycomb-white.svg" alt="Honeycomb Logo"/>
      </a>
    </div>
    <div class="flix-header-burger-menu">
      <button type="button" aria-label="Open main site navigation" class="flix-header-burger-menu__toggle flix-btn flix-btn--link flix-btn--square flix-btn--md" aria-controls="menu-panel" aria-expanded="false"></button>
      <div id="menu-panel" class="flix-header-burger-menu__panel" hidden="">
        <button type="button" aria-label="Close main site navigation" class="flix-header-burger-menu__toggle flix-btn flix-btn--link flix-btn--square flix-btn--md" aria-controls="menu-panel" aria-expanded="false"></button>
        
        <nav class="flix-header-burger-menu__nav" aria-label="Main">
          <ul class="flix-header-burger-menu__list">
            <li class="flix-header-burger-menu__item">
              <a class="flix-header-burger-menu__link" aria-current="page" href="/">
                <i class="flix-icon flix-icon-home-solid" aria-hidden="true"></i>
                <span class="flix-header-burger-menu__text">My bookings</span>
              </a>
            </li>
            <li class="flix-header-burger-menu__item">
              <button type="button" class="flix-header-burger-menu__link" data-dropdown="flix-burger-subnav">
                <i class="flix-icon flix-icon-location-solid" aria-hidden="true"></i>
                <span class="flix-header-burger-menu__text">Plan trip</span>
              </button>
              <ul id="flix-burger-subnav" class="flix-header-burger-menu-subnav" hidden="">
                <li class="flix-header-burger-menu-subnav__item">
                  <a class="flix-header-burger-menu-subnav__link" aria-current="page" href="/">
                    <i class="flix-icon flix-icon-trip-solid" aria-hidden="true"></i>
                    <span class="flix-header-burger-menu__text">Route Network</span>
                  </a>
                </li>
                <li class="flix-header-burger-menu-subnav__item">
                  <a class="flix-header-burger-menu-subnav__link" href="/">
                    <i class="flix-icon flix-icon-pin" aria-hidden="true"></i>
                    <span class="flix-header-burger-menu__text">Coach destination</span>
                  </a>
                </li>
                <li class="flix-header-burger-menu-subnav__item">
                  <a class="flix-header-burger-menu-subnav__link" href="/">
                    <span class="flix-header-burger-menu__text">Night buses</span>
                  </a>
                </li>
              </ul>
            </li>
            <li class="flix-header-burger-menu__item">
              <a class="flix-header-burger-menu__link" href="/">
                <i class="flix-icon flix-icon-real-time" aria-hidden="true"></i>
                <span class="flix-header-burger-menu__text">Real-time</span>
              </a>
            </li>
            <li class="flix-header-burger-menu__item">
              <a class="flix-header-burger-menu__link" href="/">
                <span class="flix-header-burger-menu__text">About Us</span>
              </a>
            </li>
          </ul>
        </nav>
        
      </div>
      <div class="flix-header-burger-menu__overlay flix-overlay"></div>
    </div>
  </div>
</header>

Burger menu widgets

Just like the header widgets area, the burger menu widgets is a container for whatever component you like, it is shown bellow the burger menu.

By default, the widgets container has no styles applied to it, if you wish to apply paddings equal to the menu, add the --has-container modifier to the widgets element.

The example bellow shows the language switcher toggle (please check the language switcher component page for working examples):

<header class="flix-header flix-header--unfixed">
  <div class="flix-header__inner">
    <div class="flix-header-brand flix-header-brand--square">
      <a class="flix-header-brand__link" href="/">
        <img class="flix-header-brand__img" width="36" height="36" src="/img/logos/svg/honeycomb-white.svg" alt="Honeycomb Logo"/>
      </a>
    </div>
    <div class="flix-header-burger-menu">
      <button type="button" aria-label="Open main site navigation" class="flix-header-burger-menu__toggle flix-btn flix-btn--link flix-btn--square flix-btn--md" aria-controls="menu-panel" aria-expanded="false"></button>
      <div id="menu-panel" class="flix-header-burger-menu__panel">
        <button type="button" aria-label="Close main site navigation" class="flix-header-burger-menu__toggle flix-btn flix-btn--link flix-btn--square flix-btn--md" aria-controls="menu-panel" aria-expanded="false"></button>
        
        <div class="flix-header-burger-menu__widgets flix-header-burger-menu__widgets--has-container">
          <div class="flix-language-switcher">
            <button type="button" class="flix-language-switcher__toggle">
              <img class="flix-language-switcher__flag" src="https://honeycomb-assets.hive.flixbus.com/honeycomb-flags-static/1.0.0/svg/de.svg" alt="Deutschland"/>
              Deutsch
            </button>
          </div>
        </div>
        
      </div>
      <div class="flix-header-burger-menu__overlay flix-overlay"></div>
    </div>
  </div>
</header>