Portfolio Website

In Progress
Developer - Nathan Lim January 2026

Overview

This site is a from-scratch portfolio designed for three distinct audiences: recruiters scanning for credentials, hiring managers evaluating technical depth, and general visitors browsing freely. The build process started with a requirements document, a weighted hosting comparison, and a site map — then moved into implementation using Astro 5 as a static site generator deployed on Cloudflare Pages.

A downloadable runbook covering the full implementation is available for anyone looking to replicate or learn from the process:

Download the Complete Portfolio Website Runbook (PDF)

Pre-Planning and Requirements

Before writing any code, a requirements framework was drafted covering information architecture, visual design constraints, content inventory, accessibility targets, and performance goals.

Core requirements included:

  • Progressive disclosure — show small, high-signal surface area first, with depth behind clear entry points
  • Role gate — visitors self-select as Recruiter, Hiring Manager, or Visitor; the home page reorders sections and highlights relevant tiles per role, with a “Skip — show me everything” default
  • Above-the-fold hierarchy — scannable in an F-pattern: name, role wedge, positioning statement, proof bullets, and one-click access to Resume and Contact
  • Category page “rabbit hole” mechanic — each section starts with a TL;DR panel, then shows Level 1 entries (cards/list rows), each expanding into Level 2 depth
  • Top-level navigation capped at 5 items: Work, Projects, Writing, Skills, About — with Resume and Contact as utility actions

The site flow diagram below shows how the role gate branches into four distinct home page variants — Recruiter, Hiring Manager, Visitor, and “Everything” (default) — each leading to the same shared pages but with different content ordering and emphasis.

Site flow diagram showing role gate branching into four home page variants with shared page hierarchy

Supporting documents:

Hosting Decision

A weighted comparison matrix evaluated 7 hosting options across 13 criteria (preview deploys, private staging, custom domain, HTTPS, performance, authoring UX, images, search, archive, contact forms, portability, ops burden, and cost predictability). Each criterion was weighted by importance to the project goals.

RankPlatformWeighted Score
1Cloudflare Pages + GitHub + Markdown90 / 100
2Netlify + GitHub + Markdown85 / 100
3Homelab: self-host + reverse proxy84 / 100
4Vercel + GitHub (Next.js-friendly)83 / 100
5WordPress.com69 / 100
6Webflow69 / 100
7Ghost(Pro)67 / 100

Cloudflare Pages scored highest due to static edge performance, PR/branch preview deployments, private staging via Cloudflare Access, custom domain with automatic HTTPS, high portability (Git + Markdown), and a predictable cost model ($0/month excluding domain at ~$10.46/year).

Weighted comparison matrix scoring 7 hosting platforms across 13 criteria

Each platform was also mapped to a simplified architecture strip showing the end-to-end flow from domain registration through hosting, content management, forms, and access control.

Architecture strips showing the domain-to-deployment flow for each hosting option

The full comparison with detailed pros/cons, architecture breakdowns, and source references is available:

Design System

The visual design follows a Trust + Clarity theme over stereotypical aesthetics. Requirements enforced during implementation:

  • Color roles defined as CSS custom properties (--bg, --surface, --text, --muted, --border, --accent, --focus-ring, --success, --warn, --error) to prevent random color usage
  • One accent color — a calming blue used sparingly for actions and highlights, with neutral colors doing most of the work
  • Typography — body text at 16–18px equivalent, line height at 1.5–1.6, long-form content constrained to 65–80ch max, one sans-serif for prose paired with one monospace for code
  • 8px spacing rhythm — all spacing decisions aligned to a 4/8px grid following Material Design 8dp guidance
  • WCAG AA contrast — minimum 4.5:1 for body text, enforced during palette selection for both light and dark themes
  • Dark mode — full dark theme via [data-theme="dark"] overrides on all design tokens, with localStorage persistence and system preference detection

Architecture

The site is fully static HTML generated at build time. Two client-side systems add interactivity without frameworks:

  1. Theme togglelocalStorage('theme') sets data-theme="dark" on <html>. An anti-flash inline script in <head> applies the attribute before first paint.
  2. Role gatesessionStorage('visitor-role') sets data-role on <html>. CSS order properties reorder home page sections, and role-specific accent borders highlight priority tiles per audience.

Both systems compose independently and the site works fully without JavaScript (default light theme, default “everything” layout).

Content Architecture

  • Data layer — TypeScript files (site.ts, work.ts, skills.ts, education.ts) serve as the single source of truth for structured data. Pages auto-read from these files.
  • Content collections — Markdown files in src/content/{writing,projects,certifications}/ with Zod schemas validated at build time via src/content.config.ts.
  • Component library — 24 Astro components following a coordinated-changes pattern: each file’s header comment documents which pages import it and what data it reads.

Infrastructure

  • Cloudflare Pages — Git-integrated static hosting with PR preview deployments
  • Cloudflare Access — OTP-gated preview URLs (production remains public)
  • Cloudflare Pages Functions — serverless contact form endpoint at functions/api/contact.js
  • Cloudflare Turnstile — bot protection on the contact form
  • Resend — outbound email API for contact form submissions
  • Pagefind — static client-side search indexed at build time
  • Cloudflare Email Routing — inbound email forwarding for the custom domain

Accessibility

Templates enforce WCAG AA behavior:

  • Properly nested headings (one H1 per page, no skipped levels)
  • Skip link for keyboard and screen reader users
  • Focus order that preserves meaning
  • Contrast meeting minimums for text and key UI elements
  • Semantic structure and landmarks so visual hierarchy is also programmatic

Performance

Designed with Core Web Vitals in mind:

  • Default to static HTML; hydrate only necessary components (search widget, theme toggle)
  • Reserve space for images and diagrams to avoid layout shifts
  • Optimized image loading with explicit width/height and loading="lazy"

Runbook

The implementation is documented in a 19-part runbook covering:

  1. Development environment setup (Node.js, Git, VS Code)
  2. Account creation with 2FA (GitHub, Cloudflare, Resend)
  3. Astro project scaffolding and content collections
  4. GitHub repository setup with dev/main branch model
  5. Cloudflare Pages deployment
  6. Custom domain and DNS configuration
  7. Cloudflare Access for preview gating
  8. Turnstile bot protection
  9. Email provider setup (Resend)
  10. Pages Functions for the contact form
  11. Pagefind static search
  12. Cloudflare Email Routing (inbound)
  13. Web Analytics
  14. Security headers configuration
  15. Medical disclaimer template
  16. Deployment workflow
  17. Rollback procedures
  18. Pricing and limits reference
  19. Final verification checklist

Plus appendices for troubleshooting Q&A, file structure reference, environment variables, and quick command reference.

Download the Complete Runbook (PDF)

Cost

Total monthly cost: $0. The only recurring expense is the domain at ~$10.46/year through Cloudflare Registrar at wholesale pricing. All services used (Pages, Access, Turnstile, Email Routing, Web Analytics, Resend free tier) fall within free-tier limits for a personal portfolio.