One model, every screen
A single items array drives the desktop bar and the off-canvas mobile drawer — one nav, no duplicated markup to keep in sync.
Free & open source · MIT
Nav, alone, is enough. Navalone takes one menu model and stands it up across every device — desktop dropdowns, mega menus and nested flyouts that fold into a buttery off-canvas drawer on mobile. No second nav to build, no dropdown logic to hand-roll.
Drag the handle → resize the frame and watch one menu adapt.
Why Navalone
Stop maintaining a desktop menu and a separate mobile menu. Navalone drives every layer — dropdowns, mega menus, nested flyouts and the mobile drawer — from a single model.
A single items array drives the desktop bar and the off-canvas mobile drawer — one nav, no duplicated markup to keep in sync.
Dropdowns, large dropdowns, arbitrarily nested flyouts and column mega menus — pick a display per submenu.
Restyle everything through CSS custom properties or a theme object. Try the live playground in the docs.
Thin, SSR-safe, tree-shakeable adapters over the same vanilla core. Framework deps stay as peerDependencies.
Roving focus, keyboard navigation, a modal drawer with scroll-lock, and ARIA wiring out of the box.
Drop in a <script> tag from a CDN, or import the typed ESM/CJS build into your toolchain.
| What you'd juggle | Doing it by hand | With Navalone |
|---|---|---|
| Desktop navigation | Build a horizontal bar with hover/click dropdowns | ✓ Included |
| Mobile navigation | Build a separate hamburger + off-canvas drawer | ✓ The same instance, automatically |
| Dropdowns & mega menus | Hand-roll open/close, positioning and focus | ✓ A declarative display per submenu |
| Multi-level menus | Track nested open state yourself | ✓ Arbitrary depth, built in |
| Keeping it in sync | Two markup trees to maintain | ✓ One data model |
| Accessibility | Wire ARIA, roving focus and scroll-lock | ✓ Out of the box |
Get started
Build-free from a CDN, typed ESM/CJS in your bundler, or a thin wrapper for your framework.
import { Navalone } from "navalone";
import "navalone/css";
new Navalone("#menu", {
logo: { text: "Acme", href: "/" },
items: [
{ label: "Pricing", href: "/pricing" },
{
label: "Products",
submenu: {
id: "products",
display: "mega",
columns: [
{ heading: "Apps", items: [{ label: "Analytics", href: "/a" }] }
]
}
}
]
}); import { Navalone } from "@navalone/react";
import "navalone/css";
<Navalone
items={items}
logo="Acme"
onSubmenuOpen={(d) => console.log(d.id)}
/>; <link rel="stylesheet"
href="https://unpkg.com/navalone/dist/navalone.css">
<menu id="menu"></menu>
<script src="https://unpkg.com/navalone/dist/navalone.global.js"></script>
<script>
new Navalone("#menu", { logo: "Acme", items });
</script> Pure HTML
Nav, alone, is enough — even in a plain HTML template. Write the menu as markup,
call new Navalone() with no config, and the same structure scales from a
couple of dropdowns to an e-commerce mega menu with deep multi-level dropdowns — then
folds into the mobile drawer, untouched.
<!-- One structure → desktop bar + mobile drawer, no second nav. -->
<menu class="mm" id="mm">
<a data-nv-logo href="/">Acme</a>
<!-- The .level-1 panel becomes the bar. -->
<div class="menu-level level-1" id="main-menu">
<ul>
<li><a href="/pricing">Pricing</a></li>
<!-- data-target opens the panel with the matching id. -->
<li><button data-target="shop">Shop</button></li>
</ul>
</div>
<!-- A multi-column mega menu: each .nv-group starts a column. -->
<div class="menu-level" id="shop" data-submenu="mega">
<ul>
<li class="nv-group">Men</li>
<li><a href="/men/shoes">Shoes</a></li>
<li><a href="/men/shirts">Shirts</a></li>
<li class="nv-group">Women</li>
<li><a href="/women/bags">Bags</a></li>
<li><a href="/women/dresses">Dresses</a></li>
</ul>
</div>
</menu>
<script src="https://unpkg.com/navalone/dist/navalone.global.js"></script>
<script>
// No items option, so Navalone parses the markup above.
new Navalone("#mm");
</script> A trigger's data-target opens the panel whose id matches. That single relationship nests to any depth — multi-level dropdowns and flyouts included.
A big e-commerce shop menu? Mark a panel data-submenu="mega" and let each .nv-group heading start a column. No JavaScript config required.
The same HTML collapses to a hamburger and a drill-down off-canvas drawer below your breakpoint. You never author a second mobile nav.
Free, open source, and framework-ready. Install in seconds.
npm install navalone