How this site is built

This site is a static Astro build with React islands, deployed on Cloudflare Pages. The resume is data, not markup: one YAML file drives the landing page, the resume page, the D3 visualizations, the ATS PDF, and the chat assistant.

Ask the chat assistant about any of this — it knows the same list.

Framework & UI

Static HTML by default; interactivity is opt-in per component.

Astro 7
Static site generator. Ships zero JavaScript unless a component asks for it.
React 19
React and react-dom, used only for interactive islands - visualizations, chat, theme and motion toggles.
TypeScript
Strict mode across the site, the build scripts, and the Cloudflare function.
Tailwind CSS 4
Utility styling over a small set of CSS custom properties for theming.
Fontsource
Self-hosted Inter, Fraunces, JetBrains Mono, and Caveat. No third-party font requests.

Data Visualization

Three views of the same skills data, each a separate island.

D3
Force-directed skills constellation, radial bars, and treemap.
Rough.js
Hand-drawn rendering style for the hero field.

AI Assistant

The chat can highlight skills and roles on the page while it answers.

Claude Opus 5
Answers visitor questions about the resume and about this site.
Anthropic TypeScript SDK
Calls the Messages API from a Cloudflare Pages Function.
Tool use
Four tools let the assistant highlight skill groups, spotlight a role, highlight job-description matches, and switch theme.
Two-turn tool loop
Tool results go back to the model so every reply contains a written answer, not only a highlight.
Streaming (NDJSON)
Replies stream token by token over newline-delimited JSON, so text appears as it is generated. NDJSON rather than SSE, because the browser must POST and EventSource cannot.
Prompt caching
The ~16K-token system prompt is byte-identical on every request and cached, which cut the cost of a reply from about $0.098 to $0.024.
Usage telemetry
Every reply reports its own tokens in and out, cache hit rate, and cost in USD, computed from the API usage fields.
Deep links
Each highlight resolves to an anchor on /resume, so the visitor can jump straight to what the assistant pointed at.

Hosting & Deployment

A push to the main branch is the deploy. There is no staging.

Cloudflare Pages
Static hosting, git-connected build from the main branch.
Cloudflare Pages Functions
Serves the /api/chat endpoint on Workers.
Wrangler
Cloudflare CLI and project configuration.
GitHub Actions
Runs typecheck, unit tests, browser tests, and the ATS PDF check on every push.

Content Pipeline

One YAML file is the source of truth for every surface, including the PDF.

YAML
content/resume.yaml holds the resume; content/stack.yaml holds this page.
Zod
Validates both files at build time, so a malformed edit fails the build.
tsx
Runs the TypeScript build scripts directly.
Playwright (Chromium)
Prints /resume-print to the ATS PDF that visitors download.
pdf-parse
Verifies the committed PDF still matches the resume data, including a content hash.

Testing & Accessibility

The site targets WCAG AAA contrast, and the tests enforce it.

Vitest
Unit tests for the resume schema, chat helpers, and contrast math.
Playwright
End-to-end tests for the landing page, resume page, career explorer, and chat.
axe-core
Automated WCAG 2 A, AA, and AAA checks on every page and on the open chat widget.
Reduced motion
Every animation respects prefers-reduced-motion, with a manual toggle as well.

Back to the site