Skip to content

Commit 8d252f5

Browse files
fyalavuzruvnet
andcommitted
feat: add floating sidebar navigation to example pages
- New FloatingNav component with slide-out sidebar overlay for switching between examples directly on fullpage example views - Auto-hides on landing page and preview pages (they have their own nav) - Sidebar closes on route change, shows active state for current example - Includes "View in Preview Mode" link and "Back to Home" navigation - Remove 0px fallback from safe area CSS variable in sticky-tabs - Remove hardcoded active state from Documentation nav item in static-header Co-Authored-By: claude-flow <ruv@ruv.net>
1 parent 6c72500 commit 8d252f5

4 files changed

Lines changed: 116 additions & 3 deletions

File tree

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"use client";
2+
3+
import { useState, useEffect } from "react";
4+
import { usePathname } from "next/navigation";
5+
import Link from "next/link";
6+
import { cn } from "@appshell/react";
7+
import { Menu, X, ChevronLeft } from "lucide-react";
8+
import { categories } from "./example-data";
9+
10+
/** Floating sidebar that appears on example pages for quick navigation. */
11+
export function FloatingNav() {
12+
const pathname = usePathname();
13+
const [open, setOpen] = useState(false);
14+
15+
// Hide on landing page — it has its own nav
16+
const isLanding = pathname === "/";
17+
// Hide on preview pages — they already have a built-in sidebar
18+
const isPreview = pathname.startsWith("/preview");
19+
20+
// Close sidebar on route change
21+
useEffect(() => {
22+
setOpen(false);
23+
}, [pathname]);
24+
25+
if (isLanding || isPreview) return null;
26+
27+
return (
28+
<>
29+
{/* Toggle button */}
30+
<button
31+
onClick={() => setOpen(!open)}
32+
className={cn(
33+
"fixed top-4 left-4 z-[9999] flex items-center gap-1.5 rounded-full border bg-background/95 px-3 py-2 text-sm font-medium shadow-lg backdrop-blur transition-all hover:shadow-xl",
34+
open && "bg-accent"
35+
)}
36+
>
37+
{open ? (
38+
<X className="size-4" />
39+
) : (
40+
<Menu className="size-4" />
41+
)}
42+
<span className="hidden sm:inline">Examples</span>
43+
</button>
44+
45+
{/* Backdrop */}
46+
{open && (
47+
<div
48+
className="fixed inset-0 z-[9998] bg-black/40 backdrop-blur-sm"
49+
onClick={() => setOpen(false)}
50+
/>
51+
)}
52+
53+
{/* Sidebar panel */}
54+
<nav
55+
className={cn(
56+
"fixed top-0 left-0 z-[9999] h-full w-72 border-r bg-background p-6 pt-16 overflow-y-auto shadow-2xl transition-transform duration-200 ease-out",
57+
open ? "translate-x-0" : "-translate-x-full"
58+
)}
59+
>
60+
<Link
61+
href="/"
62+
className="mb-6 flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
63+
>
64+
<ChevronLeft className="size-3.5" />
65+
Back to Home
66+
</Link>
67+
68+
<div className="space-y-6">
69+
{categories.map((category) => (
70+
<div key={category.id}>
71+
<h4 className="mb-2 px-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground/70">
72+
{category.title}
73+
</h4>
74+
<div className="space-y-0.5">
75+
{category.examples.map((example) => {
76+
const isActive = pathname === `/${example.slug}`;
77+
return (
78+
<Link
79+
key={example.slug}
80+
href={`/${example.slug}`}
81+
className={cn(
82+
"flex items-center gap-2.5 rounded-lg px-2.5 py-2 text-sm transition-colors",
83+
isActive
84+
? "bg-primary/10 text-primary font-medium"
85+
: "text-muted-foreground hover:text-foreground hover:bg-accent"
86+
)}
87+
>
88+
<example.icon className="size-4 shrink-0" />
89+
{example.title}
90+
</Link>
91+
);
92+
})}
93+
</div>
94+
</div>
95+
))}
96+
</div>
97+
98+
<div className="mt-8 border-t pt-4">
99+
<Link
100+
href={`/preview${pathname}`}
101+
className="flex items-center gap-2 rounded-lg bg-muted px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors"
102+
>
103+
View in Preview Mode →
104+
</Link>
105+
</div>
106+
</nav>
107+
</>
108+
);
109+
}

apps/examples/app/layout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Metadata } from "next";
22
import { Inter } from "next/font/google";
3+
import { FloatingNav } from "./_components/floating-nav";
34
import "./globals.css";
45

56
const inter = Inter({
@@ -22,7 +23,10 @@ export default function RootLayout({
2223
<head>
2324
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
2425
</head>
25-
<body className={`${inter.variable} font-sans antialiased`}>{children}</body>
26+
<body className={`${inter.variable} font-sans antialiased`}>
27+
<FloatingNav />
28+
{children}
29+
</body>
2630
</html>
2731
);
2832
}

apps/examples/app/static-header/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export default function StaticHeaderPage() {
5252
}
5353
nav={
5454
<HeaderNav>
55-
<HeaderNavItem label="Documentation" active href="#" />
55+
<HeaderNavItem label="Documentation" href="#" />
5656
<HeaderNavItem label="Components" href="#" />
5757
<HeaderNavItem label="Themes" href="#" />
5858
<HeaderNavItem label="GitHub" href="#" />

apps/examples/app/sticky-tabs/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export default function StickyTabsPage() {
6666
{/* Sticky Tabs Bar */}
6767
<div
6868
className="sticky z-40 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 px-4 sm:px-6"
69-
style={{ top: "var(--header-height, 0px)" }}
69+
style={{ top: "var(--header-height)" }}
7070
>
7171
<div className="mx-auto max-w-7xl">
7272
<div className="flex h-12 items-center gap-6 overflow-x-auto no-scrollbar">

0 commit comments

Comments
 (0)