Overview
Design Docs is a small layer on top of the existing portfolio. No second content system, no new Markdown engine — it reuses projects.ts, mdx.tsx, buttons, cards, and theme.
Goal
One source of truth (designDocs[]), source-agnostic rendering (local → MDX, notion → external link).
How It Fits
| Piece | File | Job |
|---|---|---|
| Type | src/lib/design-docs.ts | DesignDoc (id, title, description, type, source, updatedAt) |
| Validation | src/lib/design-docs.ts | Unique id, local/notion only, URL check for Notion, file exists for local |
| Merge | src/lib/projects.ts | Frontmatter designDocs + meta.yaml → project.designDocs |
| Index | src/app/projects/[slug]/design-docs/page.tsx | Lists docs as cards |
| Doc page | src/app/projects/[slug]/design-docs/[docId]/page.tsx | Renders MDX via MDXContent |
| Card | src/components/DesignDocCard.tsx | Shared card UI, external badge for Notion |
Data Flow
- Build scans
projects/withfast-glob+gray-matter. - For each slug, it tries
content/design-docs/<slug>/meta.yaml. If missing → no docs. - Frontmatter and YAML arrays are merged. Duplicate
idthrows at build time. project.designDocsis now the single array the UI reads.
No project-specific if — the UI just receives DesignDoc[].
Routes
/projects/<slug> → project page
/projects/<slug>/design-docs → index (all docs)
/projects/<slug>/design-docs/<docId> → local MDX doc (notion never has this)
generateStaticParams only emits local docs, so Notion docs never get a dead internal page — they are external links from the index.
Extending
Add a new source without touching UI:
type DesignDocType = "local" | "notion" | "github"
Teach the loader where to find github and the card how to label it. Cards stay source-agnostic.
Keep it simple
Metadata in YAML, content in MDX, Notion in a URL. Nothing else to configure.