Free & open source · MIT

One nav.
Every screen.

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.

px

Drag the handle → resize the frame and watch one menu adapt.

Why Navalone

One nav is all you ship.

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.

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.

Every submenu style

Dropdowns, large dropdowns, arbitrarily nested flyouts and column mega menus — pick a display per submenu.

Themeable with --nv-* tokens

Restyle everything through CSS custom properties or a theme object. Try the live playground in the docs.

React, Vue & Angular wrappers

Thin, SSR-safe, tree-shakeable adapters over the same vanilla core. Framework deps stay as peerDependencies.

Accessible by default

Roving focus, keyboard navigation, a modal drawer with scroll-lock, and ARIA wiring out of the box.

Build-free or bundled

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

Drop it in however you build.

Build-free from a CDN, typed ESM/CJS in your bundler, or a thin wrapper for your framework.

main.ts Vanilla / TypeScript
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" }] }
                ]
            }
        }
    ]
});
App.tsx React
import { Navalone } from "@navalone/react";
import "navalone/css";

<Navalone
    items={items}
    logo="Acme"
    onSubmenuOpen={(d) => console.log(d.id)}
/>;
index.html CDN
<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

One structure. Every menu. Every screen.

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.

index.html Pure HTML
<!-- 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>
  • One pairing wires it all

    A trigger's data-target opens the panel whose id matches. That single relationship nests to any depth — multi-level dropdowns and flyouts included.

  • Mega menus in plain markup

    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.

  • Folds into the drawer

    The same HTML collapses to a hamburger and a drill-down off-canvas drawer below your breakpoint. You never author a second mobile nav.

  • Read the pure-HTML guide

One nav. Ship it everywhere.

Free, open source, and framework-ready. Install in seconds.

npm install navalone