01 / OVERVIEW
This portfolio is a content-driven Next.js application that presents the projects, technologies, and technical direction I am working on, and lets me publish all of it without editing code.
Every visible string on the site is a JSON document in `content/`, validated by Zod at build time. Nothing is hardcoded inside a component, so a change to the copy is a change to data rather than a redeploy of logic.
02 / PROBLEM
Work-in-progress projects change faster than presentation code does. A portfolio that keeps its content inside components needs a code change and a redeploy for every correction to a sentence, which quietly discourages writing copy that was never tested against a reader.
There is a second problem specific to a developer in training. The honest version of a portfolio shows unfinished work, current direction, and the reasoning behind decisions. That is a large amount of prose to keep current, and maintaining it by hand is the reason projects stop being documented at all.
03 / SOLUTION
The site separates content from presentation. Four documents in `content/` hold all public copy, validated by Zod so a malformed value fails the build rather than shipping. Presentational components read those documents through cached loaders and keep the same shapes regardless of where the data came from.
A password-protected admin area writes those same documents as commits to this repository through the GitHub API, and that commit triggers the Vercel build which publishes the change. This provides a normal editing surface without introducing a database, a separate CMS, or another service to maintain. Unsubmitted form input is preserved locally, so a refresh or a rejected save does not lose work in progress.
04 / ARCHITECTURE
Next.js App Router, with the public pages prerendered at build time and the admin routes rendered on demand behind a session check. Public pages read the cached content loaders. The admin reads and writes through Server Actions, each of which validates the session itself because a Server Action is a public HTTP endpoint and its id is not a secret.
Content is described by Zod schemas in `lib/`, and the TypeScript types are derived from those schemas so the two cannot drift apart. Writes go through the GitHub Git Data API so that a project and its images land in a single commit. Styling is one hand written stylesheet in `app/globals.css`, using CSS custom properties for the design tokens.
05 / TECHNICAL DECISIONS
- Keep content in JSON documents validated by Zod rather than in typed modules, so publishing is a data change and an invalid value fails the build instead of rendering.
- Write as a commit through the GitHub API rather than to a database, which keeps the repository the source of truth and adds no service to run or pay for.
- Publish a project and its images in one commit, so a case study can never reference an image that was never written.
- Prerender the public pages and read NEXT_PUBLIC_* values at build time, so the public site works with no environment variables at all.
- Render owner-written prose by splitting on blank lines rather than emitting raw HTML, which would silently discard the structure typed into the editor.
06 / FEATURES
- Public portfolio sections rendered from validated JSON content
- Case study pages with per-section cards, images, and a capped reading measure
- Password-protected admin area for projects and every piece of public copy
- Publishing as a repository commit, which triggers the production deploy
- Client-side image resize and WebP conversion before upload
- Preserved form drafts across refreshes, failed saves, and navigation
- Accessible mobile navigation, sitemap, and social metadata