Cascading Flyouts

About this block

Cascading FlyoutsPRO

A bar with a wordmark, a Services trigger, plain links and two buttons. Services drops a compact list where items with a chevron open a second-level flyout to their right on hover, click or Arrow Right, and Arrow Left steps back. On phones a panel slides down under the bar with a two-level accordion. Both levels and the mobile panel stay in the markup and are toggled with CSS.

A bar with a wordmark, a Services trigger, plain links and two buttons. Services drops a compact list whose chevron items open a second-level flyout to the right, driven by hover, click or the arrow keys, and on phones a panel slides down with a two-level accordion.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://beste.dev/r/navbar94?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://beste.dev/r-base/navbar94?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/navbar94.tsx plus the shadcn button primitive it uses.

Quick start

The installed file exports navbar94Demo alongside the block: the exact props behind the preview above. Spread it to get a working navbar in one line.

tsx
import { Navbar94, navbar94Demo } from "@/components/beste/block/navbar94";

export default function Page() {
  return <Navbar94 {...navbar94Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Navbar94 } from "@/components/beste/block/navbar94";

export default function Page() {
  return (
    <Navbar94
      brand="Meridian"
      services={{
        label: "Services",
        items: [
          {
            label: "Strategy",
            href: "/strategy",
            description: "Where to play and how to win there.",
            children: [
              { label: "Market research", href: "/strategy/research" },
              { label: "Positioning", href: "/strategy/positioning" },
            ],
          },
          { label: "Workshops", href: "/workshops" },
        ],
      }}
      links={[
        { label: "Work", href: "/work" },
        { label: "About", href: "/about" },
      ]}
      buttons={[
        { label: "Contact sales", href: "/contact", variant: "ghost" },
        { label: "Book a call", href: "/book" },
      ]}
      labels={{ overview: "Overview", open: "Open menu", close: "Close menu" }}
    />
  );
}

Props

PropTypeDefaultDescription
brandstring–Wordmark on the left
servicesServicesMenu–Trigger label and the first-level items; items with children get a flyout
linksNavItem[][]Plain links after the trigger
buttonsActionButton[][]Buttons on the right, stacked full width in the mobile panel
labels{ overview?: string; open?: string; close?: string }{}Label of the link to the parent page in each flyout, and accessible names for the menu toggle
classNamestring–Extra classes for the outer <header>
ts
type ServicesMenu = {
  label: string;
  items: ServiceItem[];
};

type ServiceItem = {
  label: string;
  href: string;
  description?: string;
  children?: NavItem[];
};

type ActionButton = {
  label: string;
  href: string;
  variant?: "default" | "outline" | "ghost" | "secondary";
};

type NavItem = {
  label: string;
  href: string;
};

Behavior notes

  • The first-level list, every flyout and the mobile panel are server-rendered and CSS-toggled, so all child links are in the HTML; a flyout only shows when both the list is open and its parent is the active one.
  • Hovering a row sets the active flyout by index, and hovering a row without children clears it, so the pointer can sweep down the list without stray flyouts.
  • Parent rows are buttons with aria-haspopup, aria-expanded and aria-controls. Arrow Right, Enter or Space opens the flyout and focuses its first link; Arrow Left inside the flyout closes it and returns focus to the parent row.
  • Escape closes both levels and the mobile panel; a click outside the header or the pointer leaving it closes the desktop menu.
  • The mobile panel and both accordion levels animate height through grid-rows-[0fr] to grid-rows-[1fr], the panel is inert while closed, and any link inside it closes it. A ghost button becomes an outline button in the panel so it still reads as a button.

More Navigation blocks

View all Navigation
PRO

navbar87

Icon Rail Flyouts

PRO

navbar120

Serif Wordmark Navbar with Phone

PRO

navbar118

Navbar With Contact Bar and Hanging Logo

Get Pro