Navbar
Navbars or navigation bars are used to provide actions to users within a website. They are typically used at the top of a page to help users navigate between different sections of the website. Navbars can also include search bars, logos, and other branding elements.
Design Hints
Accessiblity Hints
Responsive Navbar With Logo and Links
This is a simple navbar. In smaller form factors (less than md
) the links in the middle will go into the next line. There is no javascript required for this to work. In the preview below, the second navbar is just to visualize the responsive behavior.
<nav class="top-0 z-10 flex flex-col border border-gray-400 bg-gray-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50"> <div class="container mx-auto flex flex-row items-center justify-between border-b border-gray-400 p-2 md:border-0 dark:border-zinc-800" > <div class="inline-flex items-center font-medium text-indigo-700 dark:text-zinc-50" > <!-- Your logo --> <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" aria-hidden="true" > <path fill="currentColor" d="M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66c.19-.34.05-.08.07-.12C8.48 10.94 10.42 7.54 13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15C12.96 17.55 11 21 11 21" ></path> </svg> <!-- Your logo --> <span class="ml-3 text-xl">ravixUI</span> </div> <div class="hidden items-center md:block md:gap-x-8 md:space-x-8"> <a href="#" class="hover:text-indigo-700">About</a> <a href="#" class="hover:text-indigo-700">Products</a> <a href="#" class="hover:text-indigo-700">Services</a> <a href="#" class="hover:text-indigo-700">Support</a> </div> <button type="button" class="inline-flex items-center gap-x-1 rounded-lg bg-indigo-700 px-4 py-2 font-semibold text-zinc-50 hover:bg-indigo-600 disabled:pointer-events-none disabled:opacity-60" > <span>Contact</span> </button> </div> <div class="m-2 mx-4 flex items-center justify-between gap-x-2 md:hidden"> <a href="#" class="hover:text-indigo-700">About</a> <a href="#" class="hover:text-indigo-700">Products</a> <a href="#" class="hover:text-indigo-700">Services</a> <a href="#" class="hover:text-indigo-700">Support</a> </div></nav>
<nav class="top-0 z-10 flex flex-col border border-gray-400 bg-gray-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50"> <div class="container mx-auto flex flex-row items-center justify-between border-b border-gray-400 p-2 md:border-0 dark:border-zinc-800" > <div class="inline-flex items-center font-medium text-indigo-700 dark:text-zinc-50" > <!-- Your logo --> <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" aria-hidden="true" > <path fill="currentColor" d="M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66c.19-.34.05-.08.07-.12C8.48 10.94 10.42 7.54 13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15C12.96 17.55 11 21 11 21" ></path> </svg> <!-- Your logo --> <span class="ml-3 text-xl">ravixUI</span> </div> <div class="hidden items-center md:block md:gap-x-8 md:space-x-8"> <a href="#" class="hover:text-indigo-700">About</a> <a href="#" class="hover:text-indigo-700">Products</a> <a href="#" class="hover:text-indigo-700">Services</a> <a href="#" class="hover:text-indigo-700">Support</a> </div> <button type="button" class="inline-flex items-center gap-x-1 rounded-lg bg-indigo-700 px-4 py-2 font-semibold text-zinc-50 hover:bg-indigo-600 disabled:pointer-events-none disabled:opacity-60" > <span>Contact</span> </button> </div> <div class="m-2 mx-4 flex items-center justify-between gap-x-2 md:hidden"> <a href="#" class="hover:text-indigo-700">About</a> <a href="#" class="hover:text-indigo-700">Products</a> <a href="#" class="hover:text-indigo-700">Services</a> <a href="#" class="hover:text-indigo-700">Support</a> </div></nav>
Design Hints
Responsive Navbar With Hamburger Menu
Experimental Release. Not recommended for use before proper testing
The responsive navbar which retains the logo, brand and a navigation menu (hamburger) on smaller screens, which can be used to toggle the menu on and off. In the preview below, the second navbar is just to visualize the responsive behavior.
<nav class="top-0 z-30 flex flex-col border border-gray-400 bg-gray-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50"> <div class="container mx-auto flex flex-row items-center justify-between border-b border-gray-400 p-2 md:border-0 dark:border-zinc-800" > <div class="inline-flex items-center font-medium text-indigo-700 dark:text-zinc-50" > <!-- Your logo --> <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" aria-hidden="true" > <path fill="currentColor" d="M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66c.19-.34.05-.08.07-.12C8.48 10.94 10.42 7.54 13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15C12.96 17.55 11 21 11 21" ></path> </svg> <!-- Your logo --> <span class="ml-3 text-xl">ravixUI</span> </div> <div class="hidden flex-row items-center md:flex md:space-x-6"> <a href="#" class="hover:text-indigo-700">About</a> <a href="#" class="hover:text-indigo-700">Products</a> <a href="#" class="hover:text-indigo-700">Services</a>
<button type="button" class="inline-flex items-center gap-x-1 rounded-lg bg-indigo-700 px-4 py-2 font-semibold text-zinc-50 hover:bg-indigo-600 disabled:pointer-events-none disabled:opacity-60" > <span>Contact</span> </button> </div> <div class="group relative inline-flex text-left"> <button type="button" class="group/button peer inline-flex w-full justify-center rounded-md p-3 text-sm font-semibold text-gray-900 outline-offset-4 outline-black ring-gray-400 hover:bg-gray-300 focus:outline-4 aria-expanded:bg-gray-200 aria-expanded:ring lg:text-base dark:text-gray-100 dark:shadow-none dark:outline-white dark:ring-zinc-700 dark:hover:bg-zinc-700 dark:aria-expanded:bg-zinc-800" id="navhtml-menu-button-an" aria-expanded="false" aria-haspopup="true" > <span class="sr-only">Menu Toggle</span> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" > <path fill="currentColor" d="M3 18h18v-2H3zm0-5h18v-2H3zm0-7v2h18V6z" ></path> </svg> </button>
<ul aria-orientation="vertical" aria-labelledby="navhtml-menu-button-an" role="menu" class="absolute right-0 top-14 z-10 mt-2 max-h-0 origin-top-right overflow-hidden rounded-md bg-gray-50 p-2 text-gray-800 opacity-0 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none peer-aria-expanded:max-h-96 peer-aria-expanded:opacity-100 motion-safe:transition-all motion-safe:duration-300 motion-safe:ease-in-out dark:bg-zinc-900 dark:text-gray-200" id="navhtml-dropdown-menu-an" aria-hidden="true" tabindex="-1" > <li tabindex="-1" role="menuitem" id="an-menu-item-0"> <a href="#" tabindex="-1" class="my-1 block px-5 py-3 text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >About</a > </li> <li tabindex="-1" role="menuitem" id="an-menu-item-1"> <a href="#" tabindex="-1" class="my-1 block px-5 py-3 text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >Products</a > </li> <li tabindex="-1" role="menuitem" id="an-menu-item-2"> <a href="#" tabindex="-1" class="my-1 block px-5 py-3 text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >Services</a > </li> <li tabindex="-1" role="menuitem" id="an-menu-item-3"> <form method="POST" id="an-menu-item-form-3" action="#"> <button type="submit" tabindex="-1" class="my-1 block w-full px-5 py-3 text-start text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" > Contact </button> </form> </li> </ul> </div> </div></nav><script> function menuInitilization() { const button = document.getElementById("navhtml-menu-button-an"); const dropdown = document.getElementById("navhtml-menu-button-an"); const dropdownItems = dropdown?.querySelectorAll("li"); button?.addEventListener("click", (event) => { event.stopPropagation(); if (button.getAttribute("aria-expanded") === "true") { closeMenu(); return; } button.setAttribute("aria-expanded", "true"); if (dropdown) { dropdown.setAttribute("aria-hidden", "false");
/** * Handles keyboard navigation and interactions for a dropdown menu. * * When the dropdown is focused, the following keyboard interactions are supported: * - Arrow Up/Down: Moves the focus to the previous/next item in the dropdown * - Escape: Closes the dropdown * - Enter: Clicks the currently focused dropdown item * - Tab: Moves focus back to the button */ if (dropdownItems) { document.documentElement.addEventListener("click", closeMenu); setTimeout(() => { dropdownItems[0].focus(); }, 200); // to prevent the focus setting from causing a stutter in the animation let tabFocus = 0;
dropdown.addEventListener("keydown", (keyboardEvent) => { const e = keyboardEvent;
if (e.key === "ArrowUp" || e.key === "ArrowDown") { e.preventDefault(); dropdownItems[tabFocus].setAttribute("tabindex", "-1"); if (e.key === "ArrowDown") { tabFocus++; // If we're at the end, go to the start if (tabFocus >= dropdownItems.length) { tabFocus = 0; } // Move left } else if (e.key === "ArrowUp") { tabFocus--; // If we're at the start, move to the end if (tabFocus < 0) { tabFocus = dropdownItems.length - 1; } } tabFocus = tabFocus % dropdownItems.length; dropdownItems[tabFocus].focus(); } else if (e.key === "Escape") { closeMenu(); return; } else if (e.key === "Enter") { dropdownItems[tabFocus].children[0]?.click(); closeMenu(); return; } else if (e.key === "ArrowLeft" || e.key === "ArrowRight") { e.preventDefault();
if (e.key === "ArrowRight") { tabFocus = dropdownItems.length - 1; } else if (e.key === "ArrowLeft") { tabFocus = 0; } tabFocus = tabFocus % dropdownItems.length; dropdownItems[tabFocus].focus(); } else if (e.key === "Tab" || e.key === "Shift+Tab") { e.preventDefault(); button.focus(); } }); } } }); } function closeMenu() { const button = document.getElementById("navhtml-menu-button-an"); const dropdown = document.getElementById("navhtml-menu-button-an");
button?.setAttribute("aria-expanded", "false"); dropdown?.setAttribute("aria-hidden", "true"); button?.focus(); document.documentElement.removeEventListener("click", closeMenu); } menuInitilization();</script>
<nav class="top-0 z-10 flex max-h-max flex-col border border-gray-400 bg-gray-50 text-zinc-700 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50"> <div class="container mx-auto flex flex-row items-center justify-between border-b border-gray-400 p-2 md:border-0 dark:border-zinc-800" > <div class="inline-flex items-center font-medium text-indigo-700 dark:text-zinc-50" > <!-- Your logo --> <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" aria-hidden="true" > <path fill="currentColor" d="M11 21h-1l1-7H7.5c-.58 0-.57-.32-.38-.66c.19-.34.05-.08.07-.12C8.48 10.94 10.42 7.54 13 3h1l-1 7h3.5c.49 0 .56.33.47.51l-.07.15C12.96 17.55 11 21 11 21" ></path> </svg> <!-- Your logo --> <span class="ml-3 text-xl">ravixUI</span> </div> <div class="hidden flex-row items-center md:flex md:space-x-6"> <a href="#" class="hover:text-indigo-700">About</a> <a href="#" class="hover:text-indigo-700">Products</a> <a href="#" class="hover:text-indigo-700">Services</a>
<button type="button" class="inline-flex items-center gap-x-1 rounded-lg bg-indigo-700 px-4 py-2 font-semibold text-zinc-50 hover:bg-indigo-600 disabled:pointer-events-none disabled:opacity-60" > <span>Contact</span> </button> </div>
<div class="group relative inline text-start md:hidden"> <button type="button" class="group/button peer inline-flex w-full justify-center rounded-md p-3 text-sm font-semibold text-gray-900 outline-offset-4 outline-black ring-gray-400 hover:bg-gray-300 focus:outline-4 aria-expanded:bg-gray-200 aria-expanded:ring lg:text-base dark:text-gray-100 dark:outline-white dark:ring-zinc-700 dark:hover:bg-zinc-700 dark:aria-expanded:bg-zinc-800" id="navastro-menu-button-an" aria-expanded="false" aria-haspopup="true" > <span class="sr-only">Menu Toggle</span> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" > <path fill="currentColor" d="M3 18h18v-2H3zm0-5h18v-2H3zm0-7v2h18V6z" ></path> </svg> </button>
<ul aria-orientation="vertical" aria-labelledby="navastro-menu-button-an" role="menu" class="absolute right-0 top-14 z-10 mt-2 max-h-0 origin-top-right overflow-hidden rounded-md bg-gray-50 p-2 text-gray-800 opacity-0 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none peer-aria-expanded:max-h-96 peer-aria-expanded:opacity-100 motion-safe:transition-all motion-safe:duration-300 motion-safe:ease-in-out dark:bg-zinc-900 dark:text-gray-200" id="navastro-dropdown-menu-an" aria-hidden="true" tabindex="-1" > <li tabindex="-1" role="menuitem" id="an-navmenuastro-item-0"> <a href="#" tabindex="-1" class="my-1 block px-5 py-3 text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >About</a > </li> <li tabindex="-1" role="menuitem" id="an-navmenuastro-item-1"> <a href="#" tabindex="-1" class="my-1 block px-5 py-3 text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >Products</a > </li> <li tabindex="-1" role="menuitem" id="an-navmenuastro-item-2"> <a href="#" tabindex="-1" class="my-1 block px-5 py-3 text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >Services</a > </li> <li tabindex="-1" role="menuitem" id="an-navmenuastro-item-3"> <form method="POST" id="an-navmenuastro-item-form-3" action="#"> <button type="submit" tabindex="-1" class="my-1 block w-full px-5 py-3 text-start text-base outline-offset-4 outline-black hover:bg-gray-50 focus:outline-4 dark:outline-white dark:hover:bg-zinc-700" >Contact</button > </form> </li> </ul> </div> </div></nav><script> function navMenuInitilizationAstro() { const button = document.getElementById( "navastro-menu-button-an", ) as HTMLButtonElement; const dropdown = document.getElementById( "navastro-dropdown-menu-an", ) as HTMLUListElement; const dropdownItems = dropdown?.querySelectorAll( "li", ) as NodeListOf<HTMLLIElement>; if (!button) return; button?.addEventListener("click", (event) => { event.stopPropagation();
if (button.getAttribute("aria-expanded") === "true") { closeMenu(); return; } button.setAttribute("aria-expanded", "true"); if (dropdown) { dropdown.setAttribute("aria-hidden", "false");
/** * Handles keyboard navigation and interactions for a dropdown menu. * * When the dropdown is focused, the following keyboard interactions are supported: * - Arrow Up/Down: Moves the focus to the previous/next item in the dropdown * - Escape: Closes the dropdown * - Enter: Clicks the currently focused dropdown item * - Tab: Moves focus back to the button */ if (dropdownItems) { document.documentElement.addEventListener("click", closeMenu); setTimeout(() => { dropdownItems[0].focus(); }, 200); // to prevent the focus setting from causing a stutter in the animation let tabFocus = 0;
dropdown.addEventListener("keydown", (keyboardEvent) => { const e = keyboardEvent as KeyboardEvent;
if (e.key === "ArrowUp" || e.key === "ArrowDown") { e.preventDefault(); dropdownItems[tabFocus].setAttribute("tabindex", "-1"); if (e.key === "ArrowDown") { tabFocus++; // If we're at the end, go to the start if (tabFocus >= dropdownItems.length) { tabFocus = 0; } // Move left } else if (e.key === "ArrowUp") { tabFocus--; // If we're at the start, move to the end if (tabFocus < 0) { tabFocus = dropdownItems.length - 1; } } tabFocus = tabFocus % dropdownItems.length; dropdownItems[tabFocus].focus(); } else if (e.key === "Escape") { closeMenu(); return; } else if (e.key === "Enter") { (dropdownItems[tabFocus].children[0] as HTMLElement)?.click(); closeMenu(); return; } else if (e.key === "ArrowLeft" || e.key === "ArrowRight") { e.preventDefault();
if (e.key === "ArrowRight") { tabFocus = dropdownItems.length - 1; } else if (e.key === "ArrowLeft") { tabFocus = 0; } tabFocus = tabFocus % dropdownItems.length; dropdownItems[tabFocus].focus(); } else if (e.key === "Tab" || e.key === "Shift+Tab") { e.preventDefault(); button.focus(); } }); } } }); } function closeMenu() { const button = document.getElementById("navastro-menu-button-an"); const dropdown = document.getElementById("navastro-dropdown-menu-an");
button?.setAttribute("aria-expanded", "false"); dropdown?.setAttribute("aria-hidden", "true"); button?.focus(); document.documentElement.removeEventListener("click", closeMenu); } document.addEventListener("astro:page-load", navMenuInitilizationAstro);</script>
Accessibility Testing Status
Component | NVDA | Windows Narrator | WAVE | Axe | IBM Equal Access |
---|---|---|---|---|---|
Responsive Navbar With Logo and Links | Yes | Yes | Yes | Yes | Yes |
Responsive Navbar With Hamburger Menu | Yes | Yes | Yes | Yes | Yes |
Since we use the <nav>
element, it is accessible out of the box. The hamburger menu component is implemented with the WAI-ARIA 1.2 design pattern. The components above implement all the required functionality mention in WAI-ARIA APG specification for menu button and WAI-ARIA APG specification for menu .
The menu supports the following keyboard navigation once opened:
Keyboard Navigation | Description |
---|---|
Arrow Up/Down | Moves the focus to the previous/next menu item. |
Escape | Closes the dropdown menu. |
Enter | Triggers the action associated with the focused menu item. |
Arrow Left/Right | Moves the focus to the first/last menu item. |
Tab or Shift + Tab | Moves the focus out of the menu and into the menu button. |