Skip to content

Commit bb0b147

Browse files
committed
fix(showcase/shell-docs): eliminate URL flicker on unscoped navigation
SidebarLink now uses storedFramework as fallback so links on the overview page go directly to /<framework>/<slug> without a RouterPivot redirect. OverviewNavItem and section cards now use SidebarLink for the same reason. FrameworkSelector "Clear selection" was navigating to /docs/<slug> (broken); fixed to /<slug>. hrefFor now preserves the current slug when switching frameworks from an unscoped page.
1 parent 5e7e1be commit bb0b147

3 files changed

Lines changed: 28 additions & 18 deletions

File tree

showcase/shell-docs/src/app/[[...slug]]/page.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
RouterPivot,
1616
} from "@/components/router-pivot";
1717
import { SidebarFrameworkSelector } from "@/components/sidebar-framework-selector";
18+
import { SidebarLink } from "@/components/sidebar-link";
1819
import { SidebarNav } from "@/components/sidebar-nav";
1920
import { StoredFrameworkHighlight } from "@/components/stored-framework-highlight";
2021
import {
@@ -264,9 +265,9 @@ function DocsOverview() {
264265
</div>
265266
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
266267
{sections.map((s) => (
267-
<Link
268+
<SidebarLink
268269
key={s.href}
269-
href={s.href}
270+
slug={s.href.slice(1)}
270271
className="group p-4 rounded-lg border border-[var(--border)] bg-[var(--bg-surface)] hover:border-[var(--accent)] transition-all"
271272
>
272273
<div className="text-sm font-semibold text-[var(--text)] group-hover:text-[var(--accent)] mb-1">
@@ -275,7 +276,7 @@ function DocsOverview() {
275276
<div className="text-xs text-[var(--text-muted)] leading-relaxed">
276277
{s.description}
277278
</div>
278-
</Link>
279+
</SidebarLink>
279280
))}
280281
</div>
281282
</div>
@@ -315,13 +316,14 @@ function OverviewNavItem({
315316
}
316317
if (node.type === "page") {
317318
return (
318-
<Link
319-
href={`/${node.slug}`}
320-
className="block py-[5px] text-[13px] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors"
321-
style={{ paddingLeft: `${indent}px` }}
322-
>
323-
{node.title}
324-
</Link>
319+
<div style={{ paddingLeft: `${indent}px` }}>
320+
<SidebarLink
321+
slug={node.slug}
322+
className="block py-[5px] text-[13px] text-[var(--text-muted)] hover:text-[var(--text-secondary)] transition-colors"
323+
>
324+
{node.title}
325+
</SidebarLink>
326+
</div>
325327
);
326328
}
327329
return (

showcase/shell-docs/src/components/framework-selector.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,17 @@ export function FrameworkSelector({
123123
if (frameworkTail !== null) {
124124
return frameworkTail ? `/${slug}/${frameworkTail}` : `/${slug}`;
125125
}
126-
// Case 2: currently on /docs/<rest> — switch to framework-scoped
126+
// Case 2: currently on /docs/<rest> — switch to framework-scoped (legacy)
127127
const docsTail = stripDocsPrefix(pathname);
128128
if (docsTail !== null && docsTail.length > 0) {
129129
return `/${slug}/${docsTail}`;
130130
}
131+
// Case 3: currently on /<unscoped-slug> (e.g. /quickstart) — preserve the
132+
// feature slug so switching frameworks keeps the user on the same topic.
133+
const unscopedTail = pathname.split("/").filter(Boolean).join("/");
134+
if (unscopedTail) {
135+
return `/${slug}/${unscopedTail}`;
136+
}
131137
// Fallback: framework landing page
132138
return `/${slug}`;
133139
}
@@ -275,9 +281,7 @@ export function FrameworkSelector({
275281
knownFrameworks,
276282
);
277283
if (frameworkTail !== null) {
278-
router.replace(
279-
frameworkTail ? `/docs/${frameworkTail}` : "/docs",
280-
);
284+
router.replace(frameworkTail ? `/${frameworkTail}` : "/");
281285
}
282286
setOpen(false);
283287
}}

showcase/shell-docs/src/components/sidebar-link.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@ export function SidebarLink({
2929
className,
3030
active,
3131
}: SidebarLinkProps) {
32-
const { framework } = useFramework();
32+
const { framework, storedFramework } = useFramework();
3333

34-
// Use the active framework when set, otherwise fall through to
35-
// /<slug>.
36-
const href = framework ? `/${framework}/${slug}` : `/${slug}`;
34+
// Prefer URL-active framework, then stored preference, then bare slug.
35+
// Using storedFramework here means sidebar links on unscoped pages (like
36+
// the root overview) navigate directly to the framework-scoped URL —
37+
// avoiding the visible RouterPivot redirect that would otherwise flicker
38+
// in the URL bar.
39+
const activeFramework = framework ?? storedFramework;
40+
const href = activeFramework ? `/${activeFramework}/${slug}` : `/${slug}`;
3741

3842
return (
3943
<Link

0 commit comments

Comments
 (0)