# URL-Driven Section Routing Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** Replace the single-page client-state accordion with URL-driven routing — each section gets its own static URL (`/intro`, `/bio`, etc.), clicking a section heading navigates between pages. **Architecture:** `app/[[...slug]]/page.tsx` is the single RSC that handles all routes. It resolves the active slug from URL params (defaulting to first section at `/`), then passes `activeSlug` down to `SectionsAccordion` (now a server component). `SectionAccordion` entity renders inactive sections as `` elements. `SidebarNav` uses `usePathname()` for active state. **Tech Stack:** Next.js 16 App Router, React 19, Vitest + RTL, TypeScript strict, Biome, `next/link`, `next/navigation`. --- ## Task 0: Commit next.config.ts fix The `output: 'export'` was already made conditional on `NODE_ENV === 'production'` to allow route handlers in dev. Commit it. **Files:** - Already modified: `next.config.ts` **Step 1: Verify file is correct** `next.config.ts` should read: ```ts import type { NextConfig } from 'next'; const isExport = process.env.NODE_ENV === 'production'; const nextConfig: NextConfig = { /* output: 'export' only applies at build time — enabling it in dev mode * breaks route handlers (incompatible with force-dynamic in Next.js 16) */ ...(isExport ? { output: 'export' } : {}), images: { unoptimized: true }, }; export default nextConfig; ``` **Step 2: Commit** ```bash git add next.config.ts git commit -m "fix: make output export build-only so dev route handlers work" ``` --- ## Task 1: Update SectionAccordion entity — onClick → href (TDD) Replace the inactive ` {isOpen && (
{items.map((item) => ( setIsOpen(false)} className="block w-full text-left brutal-border bg-ochre-clay px-4 py-3" >
{item.number} {item.label}
))}
)} ); } ``` **Step 4: Run tests — verify they pass** ```bash yarn test src/widgets/Navigation/ui/MobileNav.test.tsx --run ``` Expected: all PASS. **Step 5: Commit** ```bash git add src/widgets/Navigation/ui/MobileNav.tsx src/widgets/Navigation/ui/MobileNav.test.tsx git commit -m "refactor: MobileNav section items use Link instead of scrollToSection" ``` --- ## Task 5: Create [[...slug]]/page.tsx and delete app/page.tsx Wire everything together with `generateStaticParams` for SSG. **Files:** - Create: `app/[[...slug]]/page.tsx` - Delete: `app/page.tsx` **Step 1: Create the route file** Create `app/[[...slug]]/page.tsx`: ```tsx import { notFound } from 'next/navigation'; import type { SectionRecord } from '$entities/Section'; import { getCollection } from '$shared/api'; import type { NavItem } from '$widgets/Navigation'; import { MobileNav, SidebarNav } from '$widgets/Navigation'; import { SectionFactory } from '$widgets/SectionFactory'; import { SectionsAccordion } from '$widgets/SectionsAccordion'; /** * Generates static params for all section pages plus the root. */ export async function generateStaticParams() { const { items: sections } = await getCollection('sections', { sort: 'order', }); return [ {}, ...sections.map((s) => ({ slug: [s.slug] })), ]; } /** * Portfolio page — handles all section routes via optional catchall. * * `/` → first section shown as active * `/{slug}` → that section shown as active */ export default async function SectionPage({ params, }: { params: Promise<{ slug?: string[] }>; }) { const { slug } = await params; const { items: sections } = await getCollection('sections', { sort: 'order', }); if (!sections.length) notFound(); const activeSlug = slug?.[0] ?? sections[0].slug; if (!sections.find((s) => s.slug === activeSlug)) notFound(); const navItems: NavItem[] = sections.map((s) => ({ id: s.slug, label: s.title, number: s.number, })); return (
); } ``` **Step 2: Delete app/page.tsx** ```bash rm app/page.tsx ``` **Step 3: TypeScript check** ```bash yarn tsc --noEmit ``` Expected: no errors. **Step 4: Commit** ```bash git add app/ git commit -m "feat: URL-driven section routing via optional catchall with generateStaticParams" ``` --- ## Task 6: Final Verification **Step 1: Full test suite** ```bash yarn test --run ``` Expected: all tests PASS. **Step 2: TypeScript check** ```bash yarn tsc --noEmit ``` Expected: no errors. **Step 3: Lint check** ```bash yarn check ``` Expected: no errors. If auto-fixable issues: `yarn check:fix` then re-run. **Step 4: Dev server smoke test** ```bash yarn dev ``` Open `http://localhost:3000` — should show first section (intro) as active, others as links. Clicking a section link should change the URL and show that section's content.