Ahmed Abdelaziz

PortfolioDesign Docs

Architecture

How the site turns markdown files into static pages — pipeline, routes, rendering, and honest gaps.

Last updated August 2026

Overview

This site eats its own dog food: it is a Next.js 16 application where the content is the filesystem. Projects are markdown files, design docs are MDX, and everything becomes static HTML at build time — with a few deliberate server-side pieces.

The one rule

Adding content never means touching code. Drop files in, push, done.


Content Pipeline

StepWhereWhat happens
Discoverysrc/lib/projects.tsfast-glob scans projects/**/*.{md,mdx}
Parsingsamegray-matter splits YAML frontmatter from the MDX body into a typed Project — apiDocsUrl is validated as an http(s) URL when present
Design-doc mergesrc/lib/design-docs.tscontent/design-docs/<slug>/meta.yaml is validated (unique ids, local/notion, URL checks) and merged — duplicate ids fail the build loudly
Renderingsrc/lib/mdx.tsxServer-side MDXRemote with GFM, callout transformation, heading slugification, and asset-URL rewriting
Assets/project-assets/[...path]A Node route serves co-located project files with a path-traversal guard and 1-year immutable cache

Slug nuance worth knowing: the URL slug comes from frontmatter (slug: field, falling back to the file name) — not the folder name. So projects/weather now/project-weather-now.md publishes at /projects/weather-now, and its design docs live in content/design-docs/weather-now/. Three names, one project — intentional but easy to trip on.


Routes

/                                        home: hero, featured projects, services, skills, contact
/projects/[slug]                         project page (statically generated)
/projects/[slug]/design-docs             doc index cards (+ API Documentation button when apiDocsUrl exists)
/projects/[slug]/design-docs/[docId]     local doc rendering (Notion docs are external links only)
/project-assets/[...path]                filesystem asset handler

Plus generated metadata routes: opengraph-image, twitter-image, sitemap.xml, robots.txt, and a PWA manifest.


Project Header Actions

The project header (ProjectLinks in src/app/projects/[slug]/page.tsx) renders a single source of truth — project.apiDocsUrl — in two places:

  • Project page — Source Code (outline) · Design Docs (outline, when docs exist) · API Documentation (outline with FileCode + ExternalLink, target="_blank") · Live Demo (solid).
  • Design Docs index — same API Documentation button appears below the subtitle when apiDocsUrl is set, so visitors who enter via /design-docs still see the external spec without backtracking.

apiDocsUrl lives on Project (src/lib/projects.ts) alongside github/demo, is parsed from frontmatter (apiDocsUrl or alias apiDocs), trimmed, and validated with new URL() (http/https only). No new route is created — the button is a conditional external link that hides entirely when the field is absent.


The MDX Toolkit

Every markdown file on the site renders through one component map:

  • Callouts — Obsidian-style > [!tip] Title blockquotes transformed at build time into ten themed types with Lucide icons.
  • Mermaid — diagrams declared inline in MDX, rendered client-side.
  • Styled defaults — tables, code, lists, links (internal links use <Link>, external ones open safely), all wrapped in scroll-reveal animations.
  • Heading anchors — every heading gets a slugified id for the table of contents and deep links.

Diagrams Without the Bundle Penalty

Mermaid is a heavy library, so it never ships in the initial JavaScript:

  1. SSR renders an empty placeholder box.
  2. After hydration, Mermaid.tsx lazy-imports the library inside an effect.
  3. Renders are serialized through a module-level queue; theme changes trigger re-render without reload.
  4. A full-screen viewer with pan/zoom/pinch opens on click.

The trade-off: diagram content isn't in the server HTML (no SEO value), which is acceptable — prose carries the meaning, diagrams illustrate it.


Theming & Motion

  • Light/dark via next-themes (system default); tokens in globals.css using Tailwind v4 @theme inline.
  • Theme toggle uses the View Transitions API for a circular wipe from the click point, with graceful fallback.
  • All animation runs through Motion with reducedMotion="user" globally — prefers-reduced-motion is respected everywhere, including Mermaid viewers.

SEO Artifacts

ArtifactSource
Per-page metadata + canonicalsNext Metadata API across layouts/pages
Share imagesGenerated 1200×630 PNGs via next/og
Structured dataJSON-LD: Person (layout), WebSite + ProfilePage (home), Article (project/doc pages)
Sitemap / robots / manifestRoute conventions, regenerated each build

Honest Gaps

  • Sitemap misses design-doc pages — only home and projects are listed today.
  • NEXT_PUBLIC_SITE_URL falls back to localhost — unset in production, canonical URLs silently degrade.
  • A dormant Letterboxd integration exists — a complete RSS-parsing "recently watched" feature (30-min cached fetch, poster extraction) sits unreferenced by any page; the site currently performs zero runtime fetching.
  • Deployment requires a Node runtime — the asset-serving route reads the filesystem per request, so this cannot be a pure static export. On Vercel this just works.

Result

A documentation-grade portfolio: git-push publishing, zero-database content, real design docs per project, and honest engineering about what's static, what's dynamic, and what's not finished.