Sidebar Toggle Navbar
Original · freenavbar with a sidebar toggle
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/navx-sidebar-toggle.json"use client";
import { useCallback, useEffect, useId, useRef, useState } from "react";
import type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
/* ---------------------------------- icons --------------------------------- */
type IconProps = { className?: string };
function IconMenu({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.9} strokeLinecap="round" aria-hidden="true" focusable="false">
<path d="M4 7h16M4 12h16M4 17h10" />
</svg>
);
}
function IconClose({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.9} strokeLinecap="round" aria-hidden="true" focusable="false">
<path d="M6 6l12 12M18 6 6 18" />
</svg>
);
}
function IconSearch({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.9} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<circle cx="11" cy="11" r="7" />
<path d="m20 20-3.2-3.2" />
</svg>
);
}
function IconBell({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M6 9a6 6 0 0 1 12 0c0 4 1.2 5.5 2 6.5H4c.8-1 2-2.5 2-6.5Z" />
<path d="M10 19a2 2 0 0 0 4 0" />
</svg>
);
}
function IconGrid({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinejoin="round" aria-hidden="true" focusable="false">
<rect x="3.5" y="3.5" width="7" height="7" rx="1.6" />
<rect x="13.5" y="3.5" width="7" height="7" rx="1.6" />
<rect x="3.5" y="13.5" width="7" height="7" rx="1.6" />
<rect x="13.5" y="13.5" width="7" height="7" rx="1.6" />
</svg>
);
}
function IconFolder({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M3.5 6.5A1.5 1.5 0 0 1 5 5h4l2 2.5h6.5A1.5 1.5 0 0 1 19 9v8.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 3 17.5V6.5Z" />
</svg>
);
}
function IconChart({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M4 20V10M10 20V4M16 20v-7M22 20H2" />
</svg>
);
}
function IconCalendar({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<rect x="3.5" y="5" width="17" height="15" rx="2" />
<path d="M3.5 9.5h17M8 3.5v3M16 3.5v3" />
</svg>
);
}
function IconInbox({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M3.5 13 6 5.5A1.5 1.5 0 0 1 7.4 4.5h9.2A1.5 1.5 0 0 1 18 5.5L20.5 13v4.5a2 2 0 0 1-2 2h-13a2 2 0 0 1-2-2V13Z" />
<path d="M3.5 13H8a1 1 0 0 1 1 1 3 3 0 0 0 6 0 1 1 0 0 1 1-1h4.5" />
</svg>
);
}
function IconUsers({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<circle cx="9" cy="8" r="3.2" />
<path d="M3.5 19a5.5 5.5 0 0 1 11 0M16 5.2a3.2 3.2 0 0 1 0 5.6M17 19a5.5 5.5 0 0 0-3-4.9" />
</svg>
);
}
function IconSettings({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M5 7h9M18 7h1M5 12h1M10 12h9M5 17h6M15 17h4" />
<circle cx="16" cy="7" r="2" />
<circle cx="8" cy="12" r="2" />
<circle cx="13" cy="17" r="2" />
</svg>
);
}
function IconBolt({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M13 3 5 13h5l-1 8 8-10h-5l1-8Z" />
</svg>
);
}
function IconArrow({ className = "h-4 w-4" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.9} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" focusable="false">
<path d="M5 12h14M13 6l6 6-6 6" />
</svg>
);
}
function BrandMark({ className = "h-5 w-5" }: IconProps) {
return (
<svg className={className} viewBox="0 0 24 24" fill="none" aria-hidden="true" focusable="false">
<path d="M4 15c2.5 0 3.2-6 5.5-6S12 15 14.5 15 17 9 20 9" stroke="currentColor" strokeWidth={2.1} strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
/* ----------------------------------- data --------------------------------- */
type NavItem = { id: string; label: string; icon: ReactNode; badge?: string };
type NavGroup = { heading: string; items: NavItem[] };
const NAV: NavGroup[] = [
{
heading: "Workspace",
items: [
{ id: "overview", label: "Overview", icon: <IconGrid /> },
{ id: "projects", label: "Projects", icon: <IconFolder />, badge: "12" },
{ id: "analytics", label: "Analytics", icon: <IconChart /> },
{ id: "schedule", label: "Schedule", icon: <IconCalendar /> },
],
},
{
heading: "Communication",
items: [
{ id: "inbox", label: "Inbox", icon: <IconInbox />, badge: "3" },
{ id: "team", label: "Team", icon: <IconUsers /> },
],
},
{
heading: "Account",
items: [{ id: "settings", label: "Settings", icon: <IconSettings /> }],
},
];
const STATS: { label: string; value: string; delta: string; good: boolean }[] = [
{ label: "Active sprints", value: "7", delta: "+2 this week", good: true },
{ label: "Avg. cycle time", value: "3.2d", delta: "-0.4d", good: true },
{ label: "Merged PRs", value: "128", delta: "+31", good: true },
{ label: "Open issues", value: "42", delta: "+5", good: false },
];
const ACTIVITY: { who: string; ring: string; action: string; target: string; time: string }[] = [
{ who: "AO", ring: "bg-emerald-500", action: "merged", target: "feat/auth-rate-limit", time: "12m" },
{ who: "DM", ring: "bg-sky-500", action: "opened", target: "Fix flaky billing e2e test", time: "34m" },
{ who: "PN", ring: "bg-violet-500", action: "commented on", target: "RFC: event-sourced audit log", time: "1h" },
{ who: "JB", ring: "bg-amber-500", action: "closed", target: "Sprint 24 retro follow-ups", time: "2h" },
];
/* --------------------------------- helpers -------------------------------- */
function getFocusable(container: HTMLElement | null): HTMLElement[] {
if (!container) return [];
const nodes = container.querySelectorAll<HTMLElement>(
'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex="-1"])'
);
return Array.from(nodes).filter((el) => el.offsetParent !== null || el === document.activeElement);
}
/* -------------------------------- component -------------------------------- */
export default function NavxSidebarToggle() {
const reduce = useReducedMotion();
const [open, setOpen] = useState(false);
const [active, setActive] = useState("overview");
const uid = useId();
const drawerId = `${uid}-drawer`;
const titleId = `${uid}-title`;
const searchId = `${uid}-search`;
const toggleRef = useRef<HTMLButtonElement>(null);
const closeRef = useRef<HTMLButtonElement>(null);
const drawerRef = useRef<HTMLElement>(null);
const close = useCallback(() => {
setOpen(false);
toggleRef.current?.focus();
}, []);
// Move focus into the drawer once it is presented.
useEffect(() => {
if (!open) return;
const t = window.setTimeout(() => closeRef.current?.focus(), 20);
return () => window.clearTimeout(t);
}, [open]);
// Global Escape safety net.
useEffect(() => {
if (!open) return;
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") close();
};
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [open, close]);
const handleDrawerKeyDown = useCallback((e: ReactKeyboardEvent<HTMLElement>) => {
if (e.key !== "Tab") return;
const focusables = getFocusable(drawerRef.current);
if (focusables.length === 0) return;
const first = focusables[0];
const last = focusables[focusables.length - 1];
const activeEl = document.activeElement;
if (e.shiftKey && activeEl === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && activeEl === last) {
e.preventDefault();
first.focus();
}
}, []);
const selectItem = useCallback((id: string) => {
setActive(id);
setOpen(false);
toggleRef.current?.focus();
}, []);
const activeLabel =
NAV.flatMap((g) => g.items).find((i) => i.id === active)?.label ?? "Overview";
const keyframes = `
@keyframes navxsb-ping { 0% { transform: scale(1); opacity: .65; } 75%, 100% { transform: scale(2.4); opacity: 0; } }
@keyframes navxsb-float { 0%, 100% { transform: translate3d(0,0,0); } 50% { transform: translate3d(0,-14px,0); } }
.navxsb-ping { animation: navxsb-ping 1.9s cubic-bezier(0,0,.2,1) infinite; }
.navxsb-float-a { animation: navxsb-float 11s ease-in-out infinite; }
.navxsb-float-b { animation: navxsb-float 14s ease-in-out infinite reverse; }
@media (prefers-reduced-motion: reduce) {
.navxsb-ping, .navxsb-float-a, .navxsb-float-b { animation: none !important; }
}
`;
return (
<section className="relative w-full bg-slate-100 px-4 py-16 text-slate-900 sm:px-6 sm:py-20 dark:bg-slate-950 dark:text-slate-100">
<style>{keyframes}</style>
<div className="mx-auto max-w-6xl">
<div
className="relative flex min-h-[640px] flex-col overflow-hidden rounded-3xl border border-slate-200 bg-white shadow-[0_30px_80px_-40px_rgba(15,23,42,0.45)] dark:border-slate-800 dark:bg-slate-900 dark:shadow-[0_30px_90px_-40px_rgba(0,0,0,0.9)]"
>
{/* ------------------------------ navbar ------------------------------ */}
<header className="relative z-20 flex items-center gap-2 border-b border-slate-200 bg-white/85 px-3 py-3 backdrop-blur-md sm:gap-3 sm:px-5 dark:border-slate-800 dark:bg-slate-900/80">
<button
ref={toggleRef}
type="button"
onClick={() => setOpen((v) => !v)}
aria-expanded={open}
aria-controls={drawerId}
aria-label={open ? "Close navigation sidebar" : "Open navigation sidebar"}
className="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-xl border border-slate-200 text-slate-700 transition-colors hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-900"
>
<IconMenu />
</button>
<div className="flex min-w-0 items-center gap-2.5">
<span className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 text-white shadow-inner">
<BrandMark className="h-5 w-5" />
</span>
<div className="min-w-0 leading-tight">
<p className="truncate text-sm font-semibold tracking-tight text-slate-900 dark:text-white">
Cadence
</p>
<p className="truncate text-[11px] text-slate-500 dark:text-slate-400">
Platform team
</p>
</div>
</div>
<div className="ml-auto flex items-center gap-2 sm:gap-3">
<div className="relative hidden md:block">
<label htmlFor={searchId} className="sr-only">
Search projects, issues and people
</label>
<span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 dark:text-slate-500">
<IconSearch className="h-4 w-4" />
</span>
<input
id={searchId}
type="search"
placeholder="Search workspace…"
className="h-10 w-56 rounded-xl border border-slate-200 bg-slate-50 pl-9 pr-3 text-sm text-slate-700 placeholder:text-slate-400 transition-colors focus:border-indigo-400 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white lg:w-72 dark:border-slate-700 dark:bg-slate-800/60 dark:text-slate-200 dark:placeholder:text-slate-500 dark:focus-visible:ring-offset-slate-900"
/>
</div>
<button
type="button"
aria-label="Notifications, 3 unread"
className="relative inline-flex h-10 w-10 items-center justify-center rounded-xl border border-slate-200 text-slate-700 transition-colors hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-900"
>
<IconBell />
<span className="absolute right-2.5 top-2.5 flex h-2.5 w-2.5">
<span className="navxsb-ping absolute inline-flex h-full w-full rounded-full bg-rose-400" />
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-rose-500 ring-2 ring-white dark:ring-slate-900" />
</span>
</button>
<button
type="button"
aria-label="Account menu for Salman Riaz"
className="inline-flex h-10 items-center gap-2 rounded-xl border border-slate-200 pl-1 pr-1 transition-colors hover:bg-slate-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white sm:pr-2.5 dark:border-slate-700 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-900"
>
<span className="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-emerald-400 to-sky-500 text-xs font-semibold text-white">
SR
</span>
<span className="hidden text-sm font-medium text-slate-700 sm:inline dark:text-slate-200">
Salman
</span>
</button>
</div>
</header>
{/* ------------------------------ content ----------------------------- */}
<main className="relative z-0 flex-1 overflow-hidden p-6 sm:p-8">
<div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden">
<div className="navxsb-float-a absolute -left-16 -top-20 h-64 w-64 rounded-full bg-indigo-400/20 blur-3xl dark:bg-indigo-500/15" />
<div className="navxsb-float-b absolute -bottom-24 right-0 h-72 w-72 rounded-full bg-violet-400/20 blur-3xl dark:bg-violet-600/15" />
</div>
<div className="relative">
<div className="flex flex-wrap items-end justify-between gap-3">
<div>
<p className="text-xs font-medium uppercase tracking-[0.18em] text-indigo-600 dark:text-indigo-400">
{activeLabel}
</p>
<h1 className="mt-1 text-2xl font-semibold tracking-tight text-slate-900 sm:text-3xl dark:text-white">
Good afternoon, Salman
</h1>
<p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
Sprint 24 wraps Friday. You have 4 reviews waiting.
</p>
</div>
<button
type="button"
className="inline-flex items-center gap-2 rounded-xl bg-slate-900 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-slate-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:bg-white dark:text-slate-900 dark:hover:bg-slate-200 dark:focus-visible:ring-offset-slate-900"
>
New project
<IconArrow className="h-4 w-4" />
</button>
</div>
<div className="mt-6 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4">
{STATS.map((s) => (
<div
key={s.label}
className="rounded-2xl border border-slate-200 bg-slate-50/70 p-4 dark:border-slate-800 dark:bg-slate-800/40"
>
<p className="text-xs font-medium text-slate-500 dark:text-slate-400">{s.label}</p>
<p className="mt-2 text-2xl font-semibold tracking-tight text-slate-900 dark:text-white">
{s.value}
</p>
<p
className={`mt-1 text-xs font-medium ${
s.good
? "text-emerald-600 dark:text-emerald-400"
: "text-rose-600 dark:text-rose-400"
}`}
>
{s.delta}
</p>
</div>
))}
</div>
<div className="mt-4 rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-900/60">
<div className="flex items-center justify-between">
<h2 className="text-sm font-semibold text-slate-900 dark:text-white">Recent activity</h2>
<span className="text-xs text-slate-400 dark:text-slate-500">Today</span>
</div>
<ul className="mt-3 divide-y divide-slate-100 dark:divide-slate-800">
{ACTIVITY.map((a) => (
<li key={a.target} className="flex items-center gap-3 py-2.5">
<span
className={`inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-[11px] font-semibold text-white ${a.ring}`}
>
{a.who}
</span>
<p className="min-w-0 flex-1 truncate text-sm text-slate-600 dark:text-slate-300">
<span className="font-medium text-slate-900 dark:text-white">{a.who === "AO" ? "Amara" : a.who === "DM" ? "Diego" : a.who === "PN" ? "Priya" : "Jonas"}</span>{" "}
{a.action}{" "}
<span className="font-medium text-slate-800 dark:text-slate-100">{a.target}</span>
</p>
<span className="shrink-0 text-xs text-slate-400 dark:text-slate-500">{a.time}</span>
</li>
))}
</ul>
</div>
</div>
</main>
{/* --------------------------- drawer + scrim ------------------------- */}
<AnimatePresence>
{open && (
<motion.div
key="navxsb-scrim"
className="absolute inset-0 z-30 bg-slate-900/45 backdrop-blur-[2px] dark:bg-black/60"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: reduce ? 0 : 0.22, ease: "easeOut" }}
onClick={close}
aria-hidden="true"
/>
)}
{open && (
<motion.aside
key="navxsb-drawer"
ref={drawerRef}
id={drawerId}
role="dialog"
aria-modal="true"
aria-labelledby={titleId}
onKeyDown={handleDrawerKeyDown}
className="absolute inset-y-0 left-0 z-40 flex w-72 max-w-[85%] flex-col border-r border-slate-200 bg-white shadow-2xl dark:border-slate-800 dark:bg-slate-900"
initial={{ x: "-100%" }}
animate={{ x: 0 }}
exit={{ x: "-100%" }}
transition={
reduce ? { duration: 0 } : { type: "spring", stiffness: 340, damping: 34 }
}
>
<div className="flex items-center justify-between border-b border-slate-200 px-4 py-4 dark:border-slate-800">
<div className="flex items-center gap-2.5">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 text-white">
<BrandMark className="h-5 w-5" />
</span>
<span id={titleId} className="text-sm font-semibold tracking-tight text-slate-900 dark:text-white">
Cadence
</span>
</div>
<button
ref={closeRef}
type="button"
onClick={close}
aria-label="Close navigation sidebar"
className="inline-flex h-9 w-9 items-center justify-center rounded-lg text-slate-500 transition-colors hover:bg-slate-100 hover:text-slate-900 focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-white dark:focus-visible:ring-offset-slate-900"
>
<IconClose />
</button>
</div>
<nav aria-label="Primary" className="flex-1 overflow-y-auto px-3 py-4">
{NAV.map((group) => (
<div key={group.heading} className="mb-5 last:mb-0">
<p className="px-3 pb-1.5 text-[11px] font-semibold uppercase tracking-[0.14em] text-slate-400 dark:text-slate-500">
{group.heading}
</p>
<ul className="space-y-0.5">
{group.items.map((item) => {
const isActive = item.id === active;
return (
<li key={item.id}>
<button
type="button"
onClick={() => selectItem(item.id)}
aria-current={isActive ? "page" : undefined}
className={`group flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-900 ${
isActive
? "bg-indigo-50 text-indigo-700 dark:bg-indigo-500/15 dark:text-indigo-300"
: "text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-300 dark:hover:bg-slate-800 dark:hover:text-white"
}`}
>
<span
className={
isActive
? "text-indigo-600 dark:text-indigo-300"
: "text-slate-400 group-hover:text-slate-600 dark:text-slate-500 dark:group-hover:text-slate-300"
}
>
{item.icon}
</span>
<span className="flex-1 text-left">{item.label}</span>
{item.badge && (
<span
className={`inline-flex min-w-[1.4rem] items-center justify-center rounded-full px-1.5 py-0.5 text-[11px] font-semibold ${
isActive
? "bg-indigo-600 text-white dark:bg-indigo-400 dark:text-slate-900"
: "bg-slate-200 text-slate-600 dark:bg-slate-700 dark:text-slate-200"
}`}
>
{item.badge}
</span>
)}
</button>
</li>
);
})}
</ul>
</div>
))}
</nav>
<div className="border-t border-slate-200 p-3 dark:border-slate-800">
<div className="rounded-2xl bg-gradient-to-br from-indigo-500 to-violet-600 p-4 text-white">
<div className="flex items-center gap-2">
<IconBolt className="h-4 w-4" />
<p className="text-sm font-semibold">Free plan</p>
</div>
<p className="mt-1 text-xs text-indigo-100">3 of 5 projects used this month.</p>
<div className="mt-2.5 h-1.5 w-full overflow-hidden rounded-full bg-white/25">
<div className="h-full w-3/5 rounded-full bg-white" />
</div>
<button
type="button"
className="mt-3 inline-flex w-full items-center justify-center gap-1.5 rounded-lg bg-white/95 px-3 py-2 text-sm font-semibold text-indigo-700 transition-colors hover:bg-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-offset-2 focus-visible:ring-offset-indigo-600"
>
Upgrade plan
<IconArrow className="h-4 w-4" />
</button>
</div>
</div>
</motion.aside>
)}
</AnimatePresence>
</div>
<p className="mx-auto mt-4 max-w-xl text-center text-xs text-slate-500 dark:text-slate-400">
Use the menu button to open the sidebar. Inside it, Tab cycles the links, Enter selects, and Esc
(or a click outside) closes it and returns focus to the button.
</p>
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Centred Logo Navbar
OriginalA three-column navbar with the brand logo centred between the primary links and the account actions, wrapped in a soft rounded card.

Left Logo Navbar With CTA
OriginalA classic left-aligned logo navbar with centred navigation links and a prominent pill call-to-action button on the right.

Navbar With Dropdown Menu
OriginalA navbar featuring a JS-free product dropdown built with native details and summary, revealing a two-item mega-menu panel with icons.

Glass Transparent Navbar
OriginalA frosted glass navbar floating over a colourful gradient hero backdrop, using backdrop blur and translucent borders for a modern overlay header.

Responsive Navbar with Mobile Menu
MITA marketing site header with a logo, inline nav links, login/register buttons, and a working hamburger toggle that reveals a stacked mobile menu below the md breakpoint. Fully keyboard/ARIA accessible with light and dark variants.

Underline Tabs
MITAn accessible underline-style tab switcher with a bottom-border active indicator and a live content panel. State-driven with proper role=tablist/tab/tabpanel and aria-selected wiring, styled for light and dark modes.

Split Button Dropdown Menu
MITA split-button control with a chevron trigger that opens a divided action menu, including a destructive delete item. Closes on outside-click and Escape, with a rotating chevron and full ARIA menu roles for both light and dark themes.

Glass Navbar
Originalglassmorphic sticky navbar

Mega Navbar
Originalnavbar with a mega dropdown

Centered Logo Navbar
Originalnavbar with centred logo and split links

Search Navbar
Originalnavbar with an expanding search

Gradient Navbar
Originalgradient navbar

