·5 min read

Technical Notes: How This Website Is Built

Next.jsTypeScriptMDXTailwindCloudflareArchitecture

This note documents the current technical architecture of this portfolio website and explains the key implementation decisions.

1) Stack and runtime model

The site is built with Next.js 16 (App Router) and TypeScript, deployed to Cloudflare Workers via the OpenNext adapter. The rendering model is:

  • statically generated pages at build time (force-static)
  • client components only where interaction is required
  • filesystem-backed content for blog posts

This keeps the core site fast and simple while still supporting richer UI interactions. Deploying to Cloudflare's edge network means pages are served close to visitors worldwide.

2) Route and feature map

The top-level routes are:

  • / home page
  • /projects project record listing
  • /blog blog index
  • /blog/[slug] individual MDX article pages
  • /about profile and timeline
  • /feed.xml RSS feed

Pages are implemented in src/app/* and composed from focused components in src/components/*.

3) Blog content pipeline

Blog posts live as .mdx files in content/blog and are discovered from disk.

At runtime/build time, metadata is extracted from frontmatter using gray-matter in src/lib/blog.ts:

export function getPostSlugs(): string[] {
  return fs
    .readdirSync(contentDirectory)
    .filter((file) => file.endsWith(".mdx"))
    .map((file) => file.replace(/\.mdx$/, ""));
}

Each post provides:

  • title
  • date
  • excerpt
  • tags

Read time is derived from content length (estimateReadTime) and shown on cards and post pages.

Blog pages are configured for full static generation:

export const dynamic = 'force-static';
export const dynamicParams = false;

This ensures all blog posts are pre-rendered at build time with no server-side runtime cost. dynamicParams = false means only known slugs from generateStaticParams are valid — unknown slugs return a 404 without hitting the server.

The post page (src/app/blog/[slug]/page.tsx) uses MDXRemote (RSC) and applies:

  • remark-gfm for tables/task lists/extended markdown
  • rehype-slug for heading ids
  • rehype-autolink-headings for clickable heading anchors
  • rehype-pretty-code with Shiki for syntax highlighting (theme: github-dark-default)

Custom MDX elements are registered in src/components/mdx/MDXComponents.tsx, including:

  • styled headings and prose primitives
  • Callout
  • CodeBlock with copy-to-clipboard
  • ImageWithCaption

4) UI system and theming

Styling is built on Tailwind CSS v4 with tokenized CSS variables in src/app/globals.css.

The theme setup has:

  • semantic tokens (--background, --foreground, --primary, etc.)
  • light and dark palettes centered on a deep forest green (#25573a) accent
  • shared utilities (gradient-text, gradient-bg, glass)
  • a subtle repeating grid background pattern for the lab aesthetic
  • typography tuning for prose rendering

Typography uses three fonts loaded via next/font:

  • Inter — headings
  • Space Grotesk — body text
  • Geist Mono — code and monospace

next-themes powers dark/light mode, and ThemeToggle avoids hydration mismatch by rendering only after mount.

5) Motion and interaction strategy

Motion uses Framer Motion, with a reusable pattern:

  • FadeIn, SlideUp, StaggerChildren wrappers for section reveals
  • hover/lift interactions in cards
  • magnetic cursor-following CTA wrapper (MagneticButton)
  • animated nav state and mobile menu transitions

Page-level transitions are handled by next-transition-router, which wires Framer Motion enter/exit animations into Next.js App Router navigation.

Accessibility/performance protection is handled through a custom useReducedMotion hook. Components short-circuit to static rendering when users prefer reduced motion.

The hero background is an interactive particle field (@tsparticles/react) loaded with next/dynamic and ssr: false, which keeps server rendering clean while preserving a rich client-side visual effect. A Three.js scene (@react-three/fiber + @react-three/drei) is also available for 3D elements via the ParticleField component.

6) Content and data sources

Current data sources are intentionally local and deterministic:

  • blog articles from content/blog/*.mdx
  • project records from src/data/projects.ts

This keeps deployment simple (no external CMS dependency) and makes version history explicit in git.

7) SEO and distribution

The website includes:

  • route-level metadata via Next.js Metadata
  • RSS feed generation at src/app/feed.xml/route.ts
  • sitemap generation via next-sitemap (postbuild script)

The feed and sitemap are both generated directly from the same local content source, so discovery artifacts stay aligned with real content.

8) Deployment and operational profile

The site is deployed to Cloudflare Workers using the @opennextjs/cloudflare adapter. The adapter translates the Next.js build output into a Cloudflare Worker, with static assets served directly from Cloudflare's edge.

The deployment pipeline is configured in open-next.config.ts and wrangler.jsonc:

  • staticAssetsIncrementalCache is used so pre-rendered pages are served as static assets without Worker invocations
  • Cloudflare Image Optimization is bound for image handling
  • nodejs_compat compatibility flag enables Node.js APIs in the Worker runtime

Build and deploy flow:

npm run dev          # local development
npm run build        # production build
npm run preview      # build + local Cloudflare preview
npm run deploy       # build + deploy to Cloudflare

Key characteristics of the current setup:

  • edge-deployed with global distribution via Cloudflare
  • fully static blog pages (no server runtime for content)
  • low operational complexity
  • easy to extend with additional MDX posts and project entries
  • animation-heavy UI with reduced-motion fallback

9) Technical tradeoffs

Current tradeoffs are deliberate:

  • Pros: fast iteration, clear file-based content model, no CMS lock-in, edge-deployed globally, cohesive design language
  • Cons: no admin/editor interface, manual content workflow, no persisted backend data

For this project stage (portfolio + technical notes), this tradeoff is appropriate and keeps maintenance overhead low.

10) Suggested next upgrades

If the site needs to scale beyond a personal portfolio, the next upgrades would be:

  1. Add structured content schemas (frontmatter validation).
  2. Add search across blog/project content.
  3. Add image optimization workflow for MDX media.
  4. Add analytics events for project and article interactions.
  5. Add end-to-end content checks in CI.

These changes can be added incrementally without replacing the current architecture.