Building navigation

Top bars, responsive menus, off-canvas panels, dropdowns, breadcrumbs and pagination — with the initialisation details that make them behave on touch.

The top bar and responsive menu

<div class="top-bar" id="siteNav">
  <div class="top-bar-left">
    <ul class="dropdown menu" data-dropdown-menu>
      <li class="menu-text">Acme</li>
      <li>
        <a href="/product/">Product</a>
        <ul class="menu vertical">
          <li><a href="/product/grid/">Grid</a></li>
          <li><a href="/product/forms/">Forms</a></li>
        </ul>
      </li>
      <li><a href="/pricing/">Pricing</a></li>
    </ul>
  </div>
  <div class="top-bar-right">
    <ul class="menu">
      <li><a href="/login/">Sign in</a></li>
      <li><button class="button small" data-toggle="offCanvas">Menu</button></li>
    </ul>
  </div>
</div>
$(document).foundation();

// Foundation's responsive toggle API: change behaviour at a breakpoint
const breakpoint = Foundation.MediaQuery.current;   // 'small' | 'medium' | 'large' | ...
Foundation.MediaQuery.atLeast('medium');            // boolean

// Re-initialise plugins for markup injected after load
const $newRegion = $(htmlString);
$('#content').append($newRegion);
$newRegion.foundation();
$newRegion.find('[data-toggler]').each(function () {
  Foundation.Toggler.reflow($(this));               // rebuild state, do not re-construct
});
PatternRequiresMobile behaviour
menuNothingHorizontal list that wraps unless made vertical
dropdown menudata-dropdown-menu + foundation.dropdownMenuClick to open at small sizes
top-barfoundation.topbarStacks the left and right regions
equalizer on the menufoundation.equalizerKeeps sibling heights aligned
⚠️
Dropdown menus open on click for touch and hover for pointer devices, decided by What-Input's data-whatinput value. If you remove the What-Input script, dropdowns on hybrid laptops stop behaving predictably — it is a real dependency, not a nicety.

Off-canvas panels

<div class="off-canvas-wrapper">
  <div class="off-canvas-wrapper-inner" data-off-canvas-wrapper>

    <div class="off-canvas position-left" id="offCanvas" data-off-canvas data-transition="overlap">
      <ul class="vertical menu" data-accordion-menu>
        <li><a href="/dashboard/">Dashboard</a></li>
        <li>
          <a href="#">Reports</a>
          <ul class="menu vertical nested">
            <li><a href="/reports/monthly/">Monthly</a></li>
            <li><a href="/reports/annual/">Annual</a></li>
          </ul>
        </li>
      </ul>
    </div>

    <div class="off-canvas-content" data-off-canvas-content>
      <div class="top-bar">
        <div class="top-bar-left">
          <button class="button" type="button" data-toggle="offCanvas">Menu</button>
        </div>
      </div>
      <main class="grid-container">
        <h1 class="h3">Dashboard</h1>
      </main>
    </div>

  </div>
</div>
// Reach the instance to drive it from code
const offCanvas = new Foundation.OffCanvas($('#offCanvas'), {
  transition: 'overlap',
  forceTo: 'left',
  closeOnClick: true,
  trapFocus: true          // keep Tab inside the panel while it is open
});

// Close it when a link inside is followed
$('#offCanvas').on('click', 'a[href]', () => {
  Foundation.OffCanvas.getInstance($('#offCanvas')).close();
});

// The events bubble, so one listener covers many panels
$('[data-off-canvas]').on('opened.zf.offcanvas', (event) => {
  console.log('opened', event.target.id);
});
  • data-off-canvas-wrapper and data-off-canvas-content are structural: without them the panel is positioned against the wrong box and slides in from nowhere.
  • data-transition="overlap" slides the panel over the content; push moves the content; reveal slides both. Push and reveal need the wrapper or the page will scroll horizontally.
  • trapFocus: true is what makes the panel usable from a keyboard — the default focus handling does not confine Tab.
  • forceTo pins the panel to one edge instead of letting Foundation choose by screen size.

Breadcrumbs, pagination and accordion menus

<nav aria-label="You are here">
  <ul class="breadcrumbs">
    <li><a href="/">Home</a></li>
    <li><a href="/reports/">Reports</a></li>
    <li class="disabled">September</li>
    <li class="current"><span>Summary</span></li>
  </ul>
</nav>

<ul class="pagination" role="navigation" aria-label="Pagination">
  <li class="pagination-previous disabled">Previous</li>
  <li class="current"><span class="show-for-sr">You are on page</span>1</li>
  <li><a href="?p=2" aria-label="Page 2">2</a></li>
  <li><a href="?p=3" aria-label="Page 3">3</a></li>
  <li class="ellipsis" aria-hidden="true"></li>
  <li><a href="?p=9" aria-label="Page 9">9</a></li>
  <li class="pagination-next"><a href="?p=2" aria-label="Next page">Next</a></li>
</ul>

<ul class="accordion-menu" data-accordion-menu>
  <li><a href="#">Getting started</a>
    <ul class="menu vertical nested">
      <li><a href="/docs/install/">Install</a></li>
      <li><a href="/docs/build/">Build</a></li>
    </ul>
  </li>
</ul>
ComponentAccessible additionWhy
Breadcrumbs.disabled, .current, aria-labelMarks the trail's end and its name
Paginationshow-for-sr text plus aria-label on linksNumbered links alone say nothing out of context
Accordion menudata-accordion-menuAdds aria-expanded to the parent link
Top barNothing — roles are yours to addThe framework does not add navigation landmarks

Once you go past two levels of navigation, a menu is the wrong control. Foundation's accordion menu will happily nest forever, but nesting beyond one level is a sign the information architecture needs work, not more markup.

FAQ

Why does my off-canvas panel not slide in?
In order of frequency: the panel is outside .off-canvas-wrapper, the content region is missing data-off-canvas-content, or the trigger's data-toggle value does not match the panel's id. Use data-open="panelId" on a button to avoid the id mismatch entirely.
How do I make the top bar sticky?
Add data-sticky-container around the bar and a sticky element inside it, then initialise $(document).foundation(). Remember that a sticky element inside an ancestor with overflow: hidden will not stick.

Interactive plugins: reveals, tabs, accordions and dropdowns Building a complete page with Foundation

Last refreshed 2026-09-18.