feat(web): Phase 11 UI/UX — TOC, dark mode toggle, /docs index

- Install rehype-slug (proper heading anchors) + github-slugger (matching TOC)
- TOC component: auto-generated from h2/h3 headings, clickable anchors
- Dark mode toggle: ThemeToggle (localStorage + system default), class-based
- /docs index page (lists all docs by section)
- Markdown.tsx uses rehype-slug for stable heading ids
- globals.css: @custom-variant dark (.dark class) for class-based dark mode
- layout.tsx: nav includes ThemeToggle

Note: per user instruction, installed real deps (rehype-slug, github-slugger,
@types/hast) instead of hacky inline any-cast hacks.
This commit is contained in:
asepharyana
2026-08-20 13:21:53 +07:00
parent b998826b71
commit 1dd16ebcba
11 changed files with 260 additions and 25 deletions
+41
View File
@@ -0,0 +1,41 @@
import Link from "next/link";
import { listDocuments } from "@mcpedia/core";
export const dynamic = "force-dynamic";
const SECTIONS = ["docs", "writeups", "research", "notes"] as const;
export default async function DocsIndexPage() {
const all = await listDocuments();
const bySection = SECTIONS.map((section) => ({
section,
docs: all.filter((d) => d.section === section),
}));
return (
<div className="space-y-8">
<h1 className="text-2xl font-semibold tracking-tight">All Documents</h1>
{bySection.map(({ section, docs }) => (
<section key={section}>
<h2 className="text-lg font-medium capitalize mb-3">{section}</h2>
{docs.length === 0 ? (
<p className="text-sm text-zinc-500">No documents in this section.</p>
) : (
<ul className="space-y-2">
{docs.map((d) => (
<li key={d.slug}>
<Link href={`/${d.slug}`} className="hover:underline">
{d.title}
</Link>
<span className="text-xs text-zinc-500 ml-2">
{d.author || "unknown"}
</span>
</li>
))}
</ul>
)}
</section>
))}
</div>
);
}