Skip to main content
Syllabus
On this page

Responsive Navigation Pattern

Badar KhalilUpdated September 30, 2026 12 min read

Responsive Navigation Patterns: Accessible and Mobile-First

Focus keyword: responsive navigation. Navigation is the part of a website that must work for everyone: on a small phone with a thumb, on a desktop with a mouse, with a keyboard and with a screen reader. In this lesson you will learn the common responsive navigation patterns, how to build a mobile-first menu with a hamburger button, how to make it accessible with aria-expanded and keyboard support, how to build an off-canvas drawer and a dropdown submenu, and how to avoid the mistakes that make menus unusable.

responsive navigation menu, hamburger menu CSS, mobile-first navbar, aria-expanded aria-controls, accessible menu button, disclosure navigation pattern, off-canvas menu, slide-in drawer, dropdown menu focus-within, bottom navigation bar, sticky header, priority plus navigation, skip link, Escape key close menu, focus management, details summary menu, popover attribute, prefers-reduced-motion, touch target 44px, aria-current page.

1. Common Responsive Navigation Patterns

  • Inline links that wrap: the simplest choice for sites with only a few links. Use flexbox with flex-wrap: wrap and gap.
  • Hamburger (toggle) menu: links are hidden behind a Menu button on small screens and shown as a row on large screens. The most common pattern.
  • Off-canvas drawer: the menu slides in from the side over a dimmed overlay.
  • Bottom tab bar: three to five main destinations fixed at the bottom of a phone, which is easy to reach with a thumb. Good for app-like sites.
  • Priority plus: show as many links as fit and move the rest to a More menu. It needs JavaScript or container queries.
  • Mega menu: large panels with many links on desktop. Convert it to an accordion on mobile.

Choose the simplest pattern that fits your number of links. Hiding navigation behind a button reduces discoverability, so do not use a hamburger on desktop where there is room.

2. Semantic HTML First

  • Wrap the main links in <nav aria-label='Main'>. Give each nav landmark a different label when a page has more than one.
  • Use a list: ul with li and a. Screen readers announce the number of items.
  • Mark the current page with aria-current='page' and style it with [aria-current='page'].
  • Add a skip link at the start of the page so keyboard users can jump to the main content.
  • Use real links for navigation and real buttons for actions like opening a menu. Never use a div as the menu button.

3. The Mobile-First Toggle Menu

Write the small screen version as the base: the menu is hidden and the button is visible. At the breakpoint, hide the button and show the menu as a row.

  • The button needs a visible text label or an aria-label. An icon alone is not enough for assistive technology.
  • Add aria-expanded='false' and aria-controls='menu-id'. JavaScript switches aria-expanded between false and true.
  • Hide the menu with display: none (or visibility: hidden or the hidden attribute) so hidden links are not reachable with the keyboard or screen readers.
  • Use the disclosure pattern, which is a button that shows and hides content. Avoid role='menu', role='menuitem' and aria-haspopup for site navigation. Those roles are for application menus and require complex keyboard handling.

4. Keyboard and Focus Behavior

  • The button must work with Enter and Space. A native button does this for free.
  • Pressing Escape should close the open menu and return focus to the button.
  • In an off-canvas drawer, move focus to the first link when opened and return it to the button when closed. A fully modal drawer should trap focus inside, and the page behind it should be inert.
  • Use :focus-visible for a clear focus ring and never remove it without a replacement.
  • Close the menu when a link is clicked in a single page site, and when the viewport grows to the desktop breakpoint.

5. Off-Canvas Drawer

Position the drawer with position: fixed, move it out of view with transform: translateX(-100%) and bring it in by removing the transform. Use visibility: hidden when closed and transition visibility together with transform, so a closed drawer is not focusable. Add an overlay element that closes the menu when clicked. Lock page scrolling with overflow: hidden on the body while open. Respect prefers-reduced-motion by removing the transition. The native <dialog> element with showModal() gives focus trapping, the Escape key and an inert background for free, and the popover attribute offers light-dismiss behavior for simpler menus.

6. Dropdown Submenus

  • Hover only is not enough. Open the submenu on :hover and on :focus-within so keyboard users can use it, and provide a button with aria-expanded for touch screens.
  • A simple no-JavaScript option is details and summary, which is keyboard accessible by default.
  • Make sure there is no gap between the parent and the submenu so the pointer does not lose hover while moving.
  • Do not build deep multi-level flyouts. Two levels are the maximum for usability.
  • Wrap hover effects in @media (hover: hover).

7. Bottom Tab Bar

Fix the bar to the bottom on small screens and add padding-bottom: env(safe-area-inset-bottom) with viewport-fit=cover so gesture bars do not cover it. Use three to five items with an icon and a short label. Add bottom padding to the page so the bar does not cover the last content. At the desktop breakpoint, change it to a normal top navigation with position: static.

8. Sticky Header

A sticky header uses position: sticky; top: 0 with a z-index from your scale. Keep it short on small screens because it uses valuable space. Combine it with scroll-margin-top on headings so anchor links do not hide under it. Make sure the header never covers the focused element, which WCAG 2.2 calls focus not obscured.

9. Touch Target Size and Spacing

Links and buttons in the navigation should be at least 44 CSS pixels tall on touch screens, with enough space between them. Use padding on the link instead of margin around it, so the whole area is clickable.

10. Test Your Navigation

  1. Use only the keyboard: Tab, Shift+Tab, Enter, Space and Escape.
  2. Zoom to 200 percent and 400 percent. The menu must still be reachable.
  3. Test with a screen reader and check that the button state is announced as expanded or collapsed.
  4. Test on a real phone with one hand.
  5. Test at in-between widths, not only at phone and desktop sizes.
  6. Test with reduced motion enabled.

11. Common Mistakes

  • Using a div or span as the menu button.
  • Forgetting aria-expanded, or forgetting to update it.
  • Hiding the menu only visually, for example with opacity 0 or off-screen positioning, so hidden links still receive focus.
  • Hover-only dropdowns that cannot be opened with keyboard or touch.
  • Using role='menu' for ordinary site navigation.
  • No Escape key support and no focus return.
  • Transitioning display none, which cannot animate. Animate transform and opacity and toggle visibility.
  • Hamburger menus on desktop when all links fit.
  • Removing the focus outline.
  • Tiny tap targets placed very close together.

Key Takeaways

Start with semantic nav markup, write the small screen version first, use a real button with aria-expanded and aria-controls for the toggle, hide closed menus from the keyboard, support Escape and focus return, never rely on hover alone, keep touch targets large, and switch to a simple inline row when there is enough space.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself HTML
Output

Press Run to execute.

Try it Yourself JAVASCRIPT
Output

Press Run to execute.

Exercise: Make a Navigation Responsive with a Menu ButtonHTML

Turn the inline navigation into a mobile-first responsive menu. Below 768px show a Menu button with aria-expanded and aria-controls and hide the links. From 768px hide the button and show the links in a row. Use a data attribute or a class to open the menu, and write the small screen styles as the base.

Try it Yourself HTML
Output

Press Run to execute.

Show expected output
A button with aria-expanded and aria-controls, menu hidden by default, toggled by JavaScript, and a @media (min-width: 768px) rule that hides the button and displays the links as a row

This is a self-check — compare your result with the expected output above.

Exercise: Add Escape Key and Focus ReturnHTML

The menu button toggles the menu, but it is missing keyboard support. Add code so that pressing Escape closes the open menu, updates aria-expanded to false and returns focus to the button. Also make sure the hidden menu cannot be reached with the Tab key.

Try it Yourself HTML
Output

Press Run to execute.

Show expected output
A keydown listener that checks for Escape, removes the open class, sets aria-expanded to false and calls btn.focus(), while the closed menu uses display: none so links are not focusable

This is a self-check — compare your result with the expected output above.

Was this page helpful?