Navigation
I removed too much client behavior. The replacement mobile menu looked simpler in code but stopped closing reliably after navigation.
Colophon
Pages, articles, notes, search data, feeds, metadata, and social cards are built from the same validated content graph. The browser receives complete pages. The contact form is the exception: it uses a queue, an idempotent durable write, retries, and a managed dead-letter path because losing a message is a different class of failure than losing an animation.
Public read path
Next.js builds every public route ahead of time. The first response contains the article or project, navigation, code, diagram source, canonical metadata, and structured data. Small client leaves handle only stateful enhancements: navigation, themes, filters, reading position, copy feedback, and search.
Vercel receives a request for a page built ahead of time.
The response already has the copy, links, metadata, and diagram source.
Small scripts add search, themes, filters, copy buttons, and Mermaid.
Writing workflow
Keystatic is the editing surface; MDX and JSON remain the versioned source. Shared Zod schemas own runtime validation and inferred public types. Automated parity checks keep authoring fields aligned with the build-time contracts.
Search and diagrams
Pages, writing, projects, topics, and series are bundled into a small static index. Searches stay in the browser.
The source stays readable without JavaScript. Mermaid loads only on an article with diagrams, shortly before one enters view.
A small script applies the saved or system theme before the first paint, which avoids a flash of the wrong colors.
Contact delivery
The public request validates, sanitizes, rate-limits, and waits only for QStash to accept the message. A signed worker then analyzes it, persists it idempotently, and sends a best-effort notification. A failed durable write returns a retryable error; exhausted deliveries remain available in QStash’s managed dead-letter queue.
Verify the token, validate the fields, and clean the input.
Redis stops bursts and repeated submissions before they become work.
QStash takes over delivery, retries, and dead-letter storage.
A verified worker analyzes the message, records it, and notifies me.
What I got wrong in v1
I removed too much client behavior. The replacement mobile menu looked simpler in code but stopped closing reliably after navigation.
Taxonomies and visitor-facing copy still lived partly in TypeScript, so the CMS was not the end-to-end authoring surface I claimed it was.
The validator assumed every useful post was a long article with several headings, sources, and a diagram. That made short notes needlessly awkward.
Home, Writing, Article, Projects, and Contact have explicit gzip budgets. Main content and navigation remain useful with scripts disabled.
The same validated records feed canonical URLs, RSS, the sitemap, social images, search, and JSON-LD. Drafts are excluded at every projection.
When a GA ID is present, typed events record useful interactions. Vercel Analytics covers page traffic; neither tool is needed to render the site.