kalmoe.com
Personal portfolio and project hub built with Next.js, Turborepo, and MDX — scaffolded in a single session with Claude Code.
The Story
I've had a domain and a vague intention to build a real personal site for years. What finally got me moving wasn't a new framework or a design idea — it was wanting a place for my kids to have their own corner of the internet.
My oldest, Hayes, has been talking about building a website. My younger son Jack wanted one too. I figured if I was going to set up subdomains for them, I should build the infrastructure right: something that could grow with the family, host my own portfolio, maybe eventually a blog, without becoming a maintenance burden.
So on February 15, 2026, I sat down with Claude Code and just... built it. One session. Five commits.
Claude Code as Collaborator
I've used LLMs to write code snippets before, but this was different. I came in with a rough idea — monorepo, a few apps, my portfolio, kids' sites — and used Claude Code to translate that into real decisions: which tools, which structure, how to wire it all together. We worked through Turborepo configuration, pnpm workspace isolation, Tailwind v4 shared themes, and the MDX content pipeline together.
The most useful thing wasn't that Claude wrote code faster. It was that I could think out loud about tradeoffs and get back something concrete to react to. Architecture decisions that might have taken a few evenings of reading docs happened in the same session as the scaffold.
Building this way taught me something I didn't expect: the conversation itself is an artifact. The back-and-forth — my answers to Claude's questions, the reasoning I articulated out loud, the choices I explained — contains context that no commit message or code comment will ever capture. That realization shaped how I approached the entire portfolio content pipeline. (See: AI-Assisted Career Curation)
The portfolio content on this site came from fifteen years of career artifacts — annual reviews, accomplishments docs, presentations, old resumes — collected, curated through a bronze-silver-gold pipeline with Claude Code, and verified through human review. That process is its own story: a case study in AI collaboration, data quality, and catching errors before they go public. (See: AI-Assisted Career Curation)
The Family Angle
Hayes and Jack each got their own subdomain on day one: hayes.kalmoe.com and jack.kalmoe.com. Both are independent static Next.js apps inside the same repo — no shared package imports, simple Tailwind, output: 'export' so there's nothing to break.
The CLAUDE.md that lives at the repo root has a whole section dedicated to Hayes. When he's ready to start building, there's a guide written for a kid: what files to edit, how Tailwind classes work, how to deploy by pushing to main. That felt like the right way to document it — not just for me, but for whoever's working in the repo next.
Technical Detail
Architecture
The site is a Turborepo monorepo with four Next.js 15 apps and two shared packages, all managed with pnpm workspaces.
apps/
web/ → kalmoe.com (portfolio + MDX content)
blog/ → blog.kalmoe.com (placeholder for now)
hayes/ → hayes.kalmoe.com (Hayes's site, static export)
jack/ → jack.kalmoe.com (Jack's site, static export)
packages/
config/ → Shared Tailwind v4 theme, tsconfig presets, ESLint configs
ui/ → Shared React components (Prose wrapper for MDX rendering)
Each app deploys as its own Vercel project, all from the same GitHub repo. Turborepo handles caching and build ordering so pnpm build at the root only rebuilds what changed.
Content Pipeline
Portfolio pages, project write-ups, and devlog entries are all MDX files. The pipeline:
gray-matterparses frontmatter from.mdxfiles incontent/next-mdx-remote@6/rsccompiles MDX in React Server Components (no client JS overhead)- The
Prosecomponent inpackages/uiwraps rendered MDX with Tailwind typography styles
No database, no CMS, no build-time API calls. Content is just files in the repo — editable in the same workflow as code.
Shared Styling
Tailwind CSS v4 runs without a config file. The shared theme lives in packages/config/tailwind-base.css and is imported into each app's globals.css. Design tokens (colors, spacing, fonts) are defined once in @theme and available everywhere.
pnpm strict isolation means import order matters: each app's globals.css imports tailwindcss first, then the shared base — otherwise the cascade breaks.
A CVE on Day One
The initial scaffold used next-mdx-remote@5. Before the first deploy, a CVE scan flagged it. The fix was straightforward — upgrade to v6, which ships an RSC-native API — but it meant updating the import style across the content pipeline at the same time as standing up the infrastructure.
It was actually a good forcing function. The v6 API (compileMDX from next-mdx-remote/rsc) is cleaner than v5, and doing the migration on day one meant never carrying the old pattern forward.
CLAUDE.md as Living Documentation
The CLAUDE.md at the repo root is the source of truth for anyone (or any AI) working in this codebase. It covers the architecture, commands, Vercel project config, how to add a new app, and the full Hayes onboarding guide.
The convention I've settled on: CLAUDE.md captures how the repo works; devlog entries capture why decisions were made. Both live in git, so the history is always there.