Dark Sidebar
Original · freedark themed dashboard sidebar
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/side-dark.json"use client";
import {
useEffect,
useId,
useMemo,
useRef,
useState,
type ReactNode,
} from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
type IconProps = { className?: string };
type Icon = (props: IconProps) => ReactNode;
type NavChild = { id: string; label: string };
type NavItem = {
id: string;
label: string;
icon: Icon;
badge?: number;
children?: NavChild[];
};
function HomeIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="M3 10.5 12 3l9 7.5" />
<path d="M5 9.5V20a1 1 0 0 0 1 1h4v-6h4v6h4a1 1 0 0 0 1-1V9.5" />
</svg>
);
}
function ChartIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="M4 20h16" />
<rect x="5" y="12" width="3" height="6" rx="1" />
<rect x="10.5" y="8" width="3" height="10" rx="1" />
<rect x="16" y="4" width="3" height="14" rx="1" />
</svg>
);
}
function UsersIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<circle cx="9" cy="8" r="3.2" />
<path d="M3.5 19a5.5 5.5 0 0 1 11 0" />
<path d="M16 5.2a3.2 3.2 0 0 1 0 6" />
<path d="M17 14.2A5.5 5.5 0 0 1 20.5 19" />
</svg>
);
}
function RevenueIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M14.5 9a2.5 2.5 0 0 0-2.5-1.5c-1.4 0-2.5.8-2.5 2s1 1.6 2.5 2 2.5.9 2.5 2-1.1 2-2.5 2A2.5 2.5 0 0 1 9.5 15" />
<path d="M12 6v1.5M12 16.5V18" />
</svg>
);
}
function FolderIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="M3 7a2 2 0 0 1 2-2h3.6a2 2 0 0 1 1.5.7L11 7h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" />
</svg>
);
}
function ReportIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="M6 3h8l4 4v14a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z" />
<path d="M13 3v5h5" />
<path d="M8.5 13h7M8.5 16.5h7" />
</svg>
);
}
function GearIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<circle cx="12" cy="12" r="3" />
<path d="M12 2.5v2.2M12 19.3v2.2M4.2 7l1.9 1.1M17.9 15.9 19.8 17M2.5 12h2.2M19.3 12h2.2M4.2 17l1.9-1.1M17.9 8.1 19.8 7" />
</svg>
);
}
function HelpIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<circle cx="12" cy="12" r="9" />
<path d="M9.6 9.4a2.4 2.4 0 0 1 4.7.6c0 1.6-2.3 2-2.3 3.4" />
<path d="M12 16.6h.01" />
</svg>
);
}
function SearchIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<circle cx="11" cy="11" r="6.5" />
<path d="m20 20-3.4-3.4" />
</svg>
);
}
function ChevronIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.8} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="m9 6 6 6-6 6" />
</svg>
);
}
function PanelIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<rect x="3.5" y="4.5" width="17" height="15" rx="2.5" />
<path d="M9.5 4.5v15" />
</svg>
);
}
function BellIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="M6 9a6 6 0 0 1 12 0c0 5 1.5 6 1.5 6H4.5S6 14 6 9Z" />
<path d="M10 19a2 2 0 0 0 4 0" />
</svg>
);
}
function LogoutIcon({ className }: IconProps) {
return (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.7} strokeLinecap="round" strokeLinejoin="round" className={className} aria-hidden="true">
<path d="M15 4h3a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2h-3" />
<path d="M10 12H3.5M6 8.5 2.5 12 6 15.5" />
</svg>
);
}
const PRIMARY_NAV: NavItem[] = [
{ id: "overview", label: "Overview", icon: HomeIcon },
{
id: "analytics",
label: "Analytics",
icon: ChartIcon,
children: [
{ id: "traffic", label: "Traffic" },
{ id: "funnels", label: "Funnels" },
{ id: "cohorts", label: "Cohorts" },
],
},
{ id: "customers", label: "Customers", icon: UsersIcon, badge: 12 },
{ id: "revenue", label: "Revenue", icon: RevenueIcon },
{
id: "projects",
label: "Projects",
icon: FolderIcon,
children: [
{ id: "active", label: "Active" },
{ id: "archived", label: "Archived" },
],
},
{ id: "reports", label: "Reports", icon: ReportIcon, badge: 3 },
];
const SECONDARY_NAV: NavItem[] = [
{ id: "settings", label: "Settings", icon: GearIcon },
{ id: "help", label: "Help & docs", icon: HelpIcon },
];
const LABELS: Record<string, string> = (() => {
const map: Record<string, string> = {};
for (const group of [PRIMARY_NAV, SECONDARY_NAV]) {
for (const item of group) {
map[item.id] = item.label;
for (const child of item.children ?? []) {
map[child.id] = child.label;
}
}
}
return map;
})();
function filterNav(items: NavItem[], q: string): NavItem[] {
if (!q) return items;
const out: NavItem[] = [];
for (const item of items) {
const selfMatch = item.label.toLowerCase().includes(q);
const kids = (item.children ?? []).filter((c) =>
c.label.toLowerCase().includes(q),
);
if (selfMatch) {
out.push(item);
} else if (kids.length > 0) {
out.push({ ...item, children: kids });
}
}
return out;
}
export default function SideDark() {
const reduce = useReducedMotion();
const baseId = useId();
const searchId = `${baseId}-search`;
const [collapsed, setCollapsed] = useState(false);
const [query, setQuery] = useState("");
const [activeId, setActiveId] = useState("overview");
const [expanded, setExpanded] = useState<Record<string, boolean>>({
analytics: true,
});
const [menuOpen, setMenuOpen] = useState(false);
const menuRef = useRef<HTMLDivElement | null>(null);
const q = query.trim().toLowerCase();
const filteredPrimary = useMemo(() => filterNav(PRIMARY_NAV, q), [q]);
const filteredSecondary = useMemo(() => filterNav(SECONDARY_NAV, q), [q]);
const resultCount = useMemo(() => {
if (!q) return 0;
let n = 0;
for (const item of [...filteredPrimary, ...filteredSecondary]) {
n += item.children ? item.children.length : 1;
}
return n;
}, [q, filteredPrimary, filteredSecondary]);
useEffect(() => {
if (!menuOpen) return;
const onDown = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setMenuOpen(false);
}
};
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") setMenuOpen(false);
};
document.addEventListener("mousedown", onDown);
document.addEventListener("keydown", onKey);
return () => {
document.removeEventListener("mousedown", onDown);
document.removeEventListener("keydown", onKey);
};
}, [menuOpen]);
const isExpanded = (id: string) => (q ? true : Boolean(expanded[id]));
const isSectionActive = (item: NavItem) =>
item.id === activeId ||
(item.children ?? []).some((c) => c.id === activeId);
const handleParentClick = (item: NavItem) => {
if (collapsed) {
setCollapsed(false);
setExpanded((prev) => ({ ...prev, [item.id]: true }));
return;
}
setExpanded((prev) => ({ ...prev, [item.id]: !prev[item.id] }));
};
const springy = reduce
? { duration: 0 }
: { type: "spring" as const, stiffness: 320, damping: 34 };
const collapseTx = reduce ? { duration: 0 } : { duration: 0.28, ease: "easeInOut" as const };
const activeLabel = LABELS[activeId] ?? "Overview";
const renderItem = (item: NavItem) => {
const Ico = item.icon;
const hasChildren = Boolean(item.children && item.children.length > 0);
const open = hasChildren && isExpanded(item.id);
const sectionActive = isSectionActive(item);
const leafActive = !hasChildren && item.id === activeId;
const subId = `${baseId}-sub-${item.id}`;
return (
<li key={item.id}>
<button
type="button"
onClick={() =>
hasChildren ? handleParentClick(item) : setActiveId(item.id)
}
aria-current={leafActive ? "page" : undefined}
aria-expanded={hasChildren ? open : undefined}
aria-controls={hasChildren && open ? subId : undefined}
title={collapsed ? item.label : undefined}
className={[
"group relative flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium outline-none transition-colors",
"focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950",
collapsed ? "justify-center" : "",
sectionActive
? "bg-white/10 text-white"
: "text-zinc-400 hover:bg-white/5 hover:text-zinc-100",
].join(" ")}
>
{leafActive ? (
<span
aria-hidden="true"
className="absolute left-0 top-1/2 h-5 w-1 -translate-y-1/2 rounded-r-full bg-gradient-to-b from-indigo-400 to-violet-500"
/>
) : null}
<Ico className="h-5 w-5 shrink-0" />
{!collapsed && (
<span className="flex-1 truncate text-left">{item.label}</span>
)}
{!collapsed && typeof item.badge === "number" && (
<span className="rounded-full bg-indigo-500/20 px-2 py-0.5 text-[11px] font-semibold text-indigo-300 ring-1 ring-inset ring-indigo-400/30">
{item.badge}
</span>
)}
{!collapsed && hasChildren && (
<ChevronIcon
className={[
"h-4 w-4 shrink-0 text-zinc-500 transition-transform duration-200",
open ? "rotate-90" : "",
].join(" ")}
/>
)}
</button>
<AnimatePresence initial={false}>
{hasChildren && open && !collapsed && (
<motion.ul
id={subId}
initial={{ height: 0, opacity: 0 }}
animate={{ height: "auto", opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={collapseTx}
className="overflow-hidden"
>
<div className="ml-5 mt-1 flex flex-col gap-0.5 border-l border-white/10 pl-3">
{(item.children ?? []).map((child) => {
const childActive = child.id === activeId;
return (
<li key={child.id}>
<button
type="button"
onClick={() => setActiveId(child.id)}
aria-current={childActive ? "page" : undefined}
className={[
"flex w-full items-center gap-2 rounded-lg px-3 py-2 text-sm outline-none transition-colors",
"focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950",
childActive
? "font-medium text-white"
: "text-zinc-400 hover:text-zinc-100",
].join(" ")}
>
<span
aria-hidden="true"
className={[
"h-1.5 w-1.5 shrink-0 rounded-full transition-colors",
childActive
? "bg-indigo-400"
: "bg-zinc-600 group-hover:bg-zinc-500",
].join(" ")}
/>
<span className="truncate">{child.label}</span>
</button>
</li>
);
})}
</div>
</motion.ul>
)}
</AnimatePresence>
</li>
);
};
return (
<section className="relative w-full bg-slate-100 px-4 py-16 dark:bg-slate-950 sm:py-20">
<style>{`
@keyframes sidedark-ping {
0% { transform: scale(1); opacity: 0.75; }
70%, 100% { transform: scale(2.4); opacity: 0; }
}
@keyframes sidedark-sheen {
0% { background-position: -140% 0; }
100% { background-position: 240% 0; }
}
.sidedark-anim-ping { animation: sidedark-ping 2.2s cubic-bezier(0,0,0.2,1) infinite; }
.sidedark-anim-sheen {
background-image: linear-gradient(110deg, transparent 20%, rgba(255,255,255,0.5) 50%, transparent 80%);
background-size: 220% 100%;
animation: sidedark-sheen 4.5s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.sidedark-anim-ping, .sidedark-anim-sheen { animation: none !important; }
}
`}</style>
<div className="mx-auto max-w-6xl">
<div className="mb-6 text-center">
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-indigo-500 dark:text-indigo-400">
Component
</p>
<h2 className="mt-2 text-2xl font-bold tracking-tight text-slate-900 dark:text-slate-50 sm:text-3xl">
Dark dashboard sidebar
</h2>
</div>
<div className="flex min-h-[640px] overflow-hidden rounded-3xl border border-slate-300/60 bg-white shadow-2xl shadow-slate-900/10 dark:border-white/10 dark:bg-zinc-900 dark:shadow-black/40">
{/* Sidebar (always dark themed) */}
<motion.aside
initial={false}
animate={{ width: collapsed ? 78 : 280 }}
transition={collapseTx}
aria-label="Primary"
className="relative flex shrink-0 flex-col overflow-hidden border-r border-white/10 bg-gradient-to-b from-zinc-950 to-zinc-900 text-zinc-100"
>
{/* Brand + collapse */}
<div className="flex items-center gap-3 px-4 py-5">
<span className="relative grid h-9 w-9 shrink-0 place-items-center overflow-hidden rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 text-white shadow-lg shadow-indigo-900/40">
<span className="absolute inset-0 sidedark-anim-sheen" aria-hidden="true" />
<svg viewBox="0 0 24 24" fill="none" className="relative h-5 w-5" aria-hidden="true">
<path d="M12 3 4 7.5v9L12 21l8-4.5v-9L12 3Z" stroke="currentColor" strokeWidth={1.6} strokeLinejoin="round" />
<path d="M12 12v9M12 12 4 7.5M12 12l8-4.5" stroke="currentColor" strokeWidth={1.6} strokeLinejoin="round" />
</svg>
</span>
{!collapsed && (
<div className="min-w-0 flex-1">
<p className="truncate text-sm font-semibold text-white">Halyard</p>
<p className="truncate text-xs text-zinc-500">Analytics workspace</p>
</div>
)}
<button
type="button"
onClick={() => setCollapsed((v) => !v)}
aria-pressed={collapsed}
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
title={collapsed ? "Expand sidebar" : "Collapse sidebar"}
className={[
"grid h-8 w-8 shrink-0 place-items-center rounded-lg text-zinc-400 outline-none transition-colors hover:bg-white/10 hover:text-white",
"focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950",
collapsed ? "mx-auto" : "",
].join(" ")}
>
<PanelIcon className={["h-5 w-5 transition-transform", collapsed ? "rotate-180" : ""].join(" ")} />
</button>
</div>
{/* Search */}
{!collapsed && (
<div className="px-4 pb-2">
<label htmlFor={searchId} className="sr-only">
Search navigation
</label>
<div className="relative">
<SearchIcon className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-zinc-500" />
<input
id={searchId}
type="text"
role="searchbox"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search…"
className="w-full rounded-xl border border-white/10 bg-white/5 py-2 pl-9 pr-3 text-sm text-zinc-100 placeholder-zinc-500 outline-none transition-colors focus:border-indigo-400/60 focus:bg-white/10 focus-visible:ring-2 focus-visible:ring-indigo-400/50"
/>
</div>
<p className="sr-only" aria-live="polite">
{q ? `${resultCount} navigation result${resultCount === 1 ? "" : "s"}` : ""}
</p>
</div>
)}
{/* Nav */}
<nav className="flex-1 overflow-y-auto px-3 py-2">
{!collapsed && (
<p className="px-3 pb-1.5 pt-2 text-[11px] font-semibold uppercase tracking-wider text-zinc-600">
Workspace
</p>
)}
<ul className="flex flex-col gap-0.5">
{filteredPrimary.map(renderItem)}
{q && filteredPrimary.length === 0 && !collapsed && (
<li className="px-3 py-2 text-sm text-zinc-500">No matches</li>
)}
</ul>
<div className="my-3 border-t border-white/10" />
{!collapsed && (
<p className="px-3 pb-1.5 pt-1 text-[11px] font-semibold uppercase tracking-wider text-zinc-600">
System
</p>
)}
<ul className="flex flex-col gap-0.5">
{filteredSecondary.map(renderItem)}
</ul>
</nav>
{/* Upgrade card */}
{!collapsed && (
<div className="px-4 pb-3">
<div className="rounded-2xl border border-white/10 bg-white/5 p-3.5">
<div className="flex items-center gap-2">
<span className="relative flex h-2 w-2">
<span className="sidedark-anim-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400" aria-hidden="true" />
<span className="relative inline-flex h-2 w-2 rounded-full bg-emerald-400" />
</span>
<p className="text-xs font-semibold text-white">Trial · 6 days left</p>
</div>
<p className="mt-1.5 text-xs leading-relaxed text-zinc-400">
Unlock unlimited seats and 24-month data retention.
</p>
<button
type="button"
className="mt-3 w-full rounded-lg bg-gradient-to-r from-indigo-500 to-violet-600 py-2 text-xs font-semibold text-white outline-none transition-opacity hover:opacity-90 focus-visible:ring-2 focus-visible:ring-indigo-300 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950"
>
Upgrade plan
</button>
</div>
</div>
)}
{/* User / account menu */}
<div ref={menuRef} className="relative border-t border-white/10 p-3">
<AnimatePresence>
{menuOpen && !collapsed && (
<motion.div
initial={{ opacity: 0, y: 8, scale: 0.98 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 8, scale: 0.98 }}
transition={springy}
role="menu"
aria-label="Account"
className="absolute bottom-[calc(100%-0.25rem)] left-3 right-3 origin-bottom rounded-xl border border-white/10 bg-zinc-800 p-1.5 shadow-2xl shadow-black/50"
>
<button
type="button"
role="menuitem"
onClick={() => setMenuOpen(false)}
className="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-zinc-200 outline-none transition-colors hover:bg-white/10 focus-visible:ring-2 focus-visible:ring-indigo-400"
>
<GearIcon className="h-4 w-4 text-zinc-400" />
Account settings
</button>
<button
type="button"
role="menuitem"
onClick={() => setMenuOpen(false)}
className="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-zinc-200 outline-none transition-colors hover:bg-white/10 focus-visible:ring-2 focus-visible:ring-indigo-400"
>
<BellIcon className="h-4 w-4 text-zinc-400" />
Notifications
</button>
<div className="my-1 border-t border-white/10" />
<button
type="button"
role="menuitem"
onClick={() => setMenuOpen(false)}
className="flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm text-rose-300 outline-none transition-colors hover:bg-rose-500/10 focus-visible:ring-2 focus-visible:ring-rose-400"
>
<LogoutIcon className="h-4 w-4" />
Sign out
</button>
</motion.div>
)}
</AnimatePresence>
<button
type="button"
onClick={() => setMenuOpen((v) => !v)}
aria-haspopup="menu"
aria-expanded={menuOpen}
title={collapsed ? "Priya Nair" : undefined}
className={[
"flex w-full items-center gap-3 rounded-xl px-2 py-2 text-left outline-none transition-colors hover:bg-white/5",
"focus-visible:ring-2 focus-visible:ring-indigo-400 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-950",
collapsed ? "justify-center" : "",
].join(" ")}
>
<span className="grid h-9 w-9 shrink-0 place-items-center rounded-full bg-gradient-to-br from-emerald-400 to-sky-500 text-sm font-bold text-zinc-900">
PN
</span>
{!collapsed && (
<>
<span className="min-w-0 flex-1">
<span className="block truncate text-sm font-semibold text-white">Priya Nair</span>
<span className="block truncate text-xs text-zinc-500">Product Lead</span>
</span>
<ChevronIcon
className={["h-4 w-4 shrink-0 text-zinc-500 transition-transform", menuOpen ? "-rotate-90" : "rotate-90"].join(" ")}
/>
</>
)}
</button>
</div>
</motion.aside>
{/* Content preview (adapts to light + dark) */}
<div className="flex min-w-0 flex-1 flex-col bg-slate-50 dark:bg-zinc-950">
<header className="flex flex-wrap items-center justify-between gap-3 border-b border-slate-200 px-6 py-5 dark:border-white/10">
<div className="min-w-0">
<h3 className="truncate text-lg font-semibold text-slate-900 dark:text-slate-50">
{activeLabel}
</h3>
<p className="text-sm text-slate-500 dark:text-zinc-400">
Tuesday, 16 July · Last synced 4 minutes ago
</p>
</div>
<div className="flex items-center gap-2">
<button
type="button"
aria-label="Notifications"
className="relative grid h-9 w-9 place-items-center rounded-xl border border-slate-200 text-slate-600 outline-none transition-colors hover:bg-slate-100 focus-visible:ring-2 focus-visible:ring-indigo-400 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/5"
>
<BellIcon className="h-5 w-5" />
<span className="absolute right-2 top-2 h-2 w-2 rounded-full bg-rose-500 ring-2 ring-slate-50 dark:ring-zinc-950" />
</button>
<button
type="button"
className="rounded-xl bg-slate-900 px-4 py-2 text-sm font-semibold text-white outline-none transition-colors hover:bg-slate-700 focus-visible:ring-2 focus-visible:ring-indigo-400 dark:bg-white dark:text-zinc-900 dark:hover:bg-zinc-200"
>
New report
</button>
</div>
</header>
<div className="grid gap-4 p-6 sm:grid-cols-2 lg:grid-cols-3">
{[
{ k: "Monthly revenue", v: "$48,210", d: "+12.4%", up: true },
{ k: "Active users", v: "12,480", d: "+3.1%", up: true },
{ k: "Churn rate", v: "1.8%", d: "-0.4%", up: false },
].map((s) => (
<div
key={s.k}
className="rounded-2xl border border-slate-200 bg-white p-5 dark:border-white/10 dark:bg-zinc-900"
>
<p className="text-sm text-slate-500 dark:text-zinc-400">{s.k}</p>
<div className="mt-2 flex items-end justify-between">
<p className="text-2xl font-bold tracking-tight text-slate-900 dark:text-slate-50">
{s.v}
</p>
<span
className={[
"rounded-full px-2 py-0.5 text-xs font-semibold",
s.up
? "bg-emerald-100 text-emerald-700 dark:bg-emerald-500/15 dark:text-emerald-400"
: "bg-rose-100 text-rose-700 dark:bg-rose-500/15 dark:text-rose-400",
].join(" ")}
>
{s.d}
</span>
</div>
</div>
))}
</div>
<div className="px-6 pb-6">
<div className="rounded-2xl border border-slate-200 bg-white p-5 dark:border-white/10 dark:bg-zinc-900">
<div className="mb-4 flex items-center justify-between">
<p className="text-sm font-semibold text-slate-900 dark:text-slate-50">
Weekly signups
</p>
<p className="text-xs text-slate-500 dark:text-zinc-400">Last 7 days</p>
</div>
<div className="flex h-32 items-end gap-2" aria-hidden="true">
{[42, 58, 51, 74, 63, 88, 96].map((h, i) => (
<div
key={i}
className="flex-1 rounded-t-md bg-gradient-to-t from-indigo-500/80 to-violet-500/80"
style={{ height: `${h}%` }}
/>
))}
</div>
<div className="mt-2 flex gap-2 text-[11px] text-slate-400 dark:text-zinc-500">
{["M", "T", "W", "T", "F", "S", "S"].map((d, i) => (
<span key={i} className="flex-1 text-center">{d}</span>
))}
</div>
</div>
</div>
</div>
</div>
</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 →
Basic Sidebar
Originalbasic app sidebar navigation

Collapsible Sidebar
Originalcollapsible sidebar sections
Icons Sidebar
Originalicon sidebar with labels on hover

Drawer Left Sidebar
Originalslide-in left drawer with overlay

Drawer Right Sidebar
Originalslide-in right drawer panel

Nested Sidebar
Originalsidebar with expandable nested items

Mini Sidebar
Originalmini rail that expands on hover

Sections Sidebar
Originalsidebar with grouped sections and headers

Mobile Sidebar
Originalmobile hamburger slide menu

Floating Sidebar
Originalfloating rounded sidebar

Rail Sidebar
Originalnarrow icon rail with tooltips

Spotlight Hero
OriginalA centred hero with a soft radial spotlight, badge and dual call-to-action.

