Building Prompt to Production: A Site That Documents Itself

This is the first post on Prompt to Production, so it seems only right that it's about how the site itself got built. Not as a tutorial — more a record of the actual process, the parts that worked and the parts that didn't, because that's the point of this blog: showing the real work, not a cleaned-up highlight reel.
From “I need a name” to a live domain
This started as a naming problem. I wanted a place to document my AI journey — the local LLM homelab stuff and the professional AI-assisted development tooling side that is changing how we work. We went back and forth on tone and style before landing on Prompt to Production, which does double duty: it's literally what the site is about, and it's a decent description of how the site itself got made.
prompttoproduction.ai was available and it captured the spirit of what this page is about. That settled it.
The stack
Nothing exotic:
- Next.js (App Router) for the frontend, deployed on Vercel
- Sanity as a headless CMS, with the Studio embedded in the same repo at /studio rather than deployed separately — one codebase, one deploy
- next-themes for dark mode
- Content is statically generated at build time, with a Sanity webhook triggering on-demand revalidation when I publish or edit — so pages stay fast without ever going stale
None of this is a novel architecture. The interesting part wasn't the stack — it was the process.
Building it with a review loop, not just a prompt
Rather than one long implementation session, the build ran as a formal plan: a design spec, broken into discrete tasks, each one handed to a fresh implementer with no memory of the rest of the project — just its task and the interfaces it needed to respect. Every task got reviewed against the spec before moving to the next one, and the whole branch got a final review before anything went live.
That structure caught real problems. A couple of examples:
- Icons that type-checked but didn't run. A dependency's TypeScript types still declared some icon exports, but the actual runtime module had dropped them. tsc was happy. The production build wasn't. The reviewer caught it by checking the installed package's actual files, not just its types.
- Pages that weren't actually static. The framework's newer default rendering mode meant pages were being built dynamically even though the plan called for static generation with on-demand revalidation. The webhook was live and correctly verifying signatures — it just had nothing to invalidate. A whole-branch review caught the gap, and the fix was verified by re-running the production build and diffing the route table, not just trusting a report that said “fixed.”
The bug that shipped anyway
Of course, something still got through: dark mode. The toggle worked — it flipped a class on the page — but the background and text colors didn't move with it. They were wired to prefers-color-scheme, a pure OS-level media query, while the toggle itself was flipping a .dark class that had nothing to do with those two variables. Two dark-mode mechanisms, only one of them actually wired to the button.
Root cause took about ten minutes once I stopped assuming and started reading the actual CSS. The fix was a one-line selector change. The lesson was less about CSS and more about the gap between “reviewed and merged” and “actually looked at in a browser.”
What's next
More posts here as I keep building — some about similar AI-assisted workflow, some about the local LLM and homelab inference side of things. If you want to see how the sausage gets made, this is that place.