A studio bar with a wordmark, a Work trigger, plain links and a button. Work opens a full-width panel with six large project links on the left; hovering or focusing one cross-fades a preview card on the right with a 4:3 photo, a title and a line. On phones a plain flat list drops under the bar. Every preview card and the mobile list stay in the markup and are toggled with CSS.
A studio bar with a wordmark, a Work trigger, plain links and a button. Work opens a full-width panel of six large project links whose preview card on the right cross-fades to whichever link is hovered or focused, and on phones a plain flat list drops under the bar.
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.
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
npx shadcn add "https://beste.dev/r/navbar95?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://beste.dev/r-base/navbar95?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/navbar95.tsx plus the shadcn button primitive it uses.
The installed file exports navbar95Demo alongside the block: the exact props behind the preview above. Spread it to get a working navbar in one line.
import { Navbar95, navbar95Demo } from "@/components/beste/block/navbar95";
export default function Page() {
return <Navbar95 {...navbar95Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Navbar95 } from "@/components/beste/block/navbar95";
export default function Page() {
return (
<Navbar95
brand="Alder"
work={{
label: "Work",
items: [
{
title: "Harbor Bank",
description: "A banking app rebuilt around one weekly check-in.",
href: "/work/harbor",
image: { src: "/photos/harbor.jpg", alt: "A laptop showing charts" },
},
{
title: "Lumen Travel",
description: "A booking flow that starts with the beach.",
href: "/work/lumen",
image: { src: "/photos/lumen.jpg", alt: "An aerial view of a beach" },
},
],
viewAll: { label: "All case studies", href: "/work" },
}}
links={[
{ label: "Studio", href: "/studio" },
{ label: "Journal", href: "/journal" },
]}
button={{ label: "Start a project", href: "/contact" }}
labels={{ open: "Open menu", close: "Close menu" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
brand | string | – | Wordmark on the left |
work | WorkMenu | – | Trigger label, the project links with their preview cards, and an optional view all link |
links | NavItem[] | [] | Plain links after the trigger |
button | NavItem | – | Small action on the right, the last row of the mobile list |
labels | { open?: string; close?: string } | {} | Accessible names for the menu toggle |
className | string | – | Extra classes for the outer <header> |
type WorkMenu = {
label: string;
items: WorkItem[];
viewAll?: NavItem;
};
type WorkItem = {
title: string;
description: string;
href: string;
image: { src: string; alt: string };
};
type NavItem = {
label: string;
href: string;
};col-start-1 row-start-1), so the column sizes to the tallest card and the active one cross-fades in with opacity over 500ms while the others take pointer-events-none.onMouseEnter and onFocus, so tabbing down the list swaps the preview just like the pointer does; it stays on the last link touched when the panel closes.inert while closed and any link closes it.