Context
I redesigned my portfolio, but I didn't want to touch code every time I added a project or fixed a typo. I also didn't want a generic site builder — it had to match my design system exactly — or to pay a website-builder platform every month.
I already write everything in Notion. So I made Notion the place I write, and used Claude Code to build the site that reads from it. Write a page in Notion, and it shows up on the site — on-brand, in minutes.
Challenges
| Challenge | Why it matters |
|---|---|
| Every update meant editing code | A new project or a one-line fix needed a code change and a deploy. Too slow to keep fresh. |
| Site builders didn't fit | Templates looked generic and couldn't match my type, spacing, and components. |
| Writing should feel like writing | Publishing a case study should be as easy as writing a Notion doc, not a coding task. |
| Consistency across pages | Every page had to follow the same design rules without me checking each one by hand. |
Goals
| Goal | Target outcome |
|---|---|
| Publish without code | Add or edit a project in Notion and see it live — no deploy. |
| Stay on-brand | Every page uses my design system: same type, spacing, components. |
| Own the whole build | Design and build it myself, end to end, fast. |
| Keep it low-maintenance | A simple stack I can run and update on my own for years. |
Results
| Metric | Result |
|---|---|
| Time to publish | Minutes — from Notion to live |
| Content I control | Case studies and articles, all edited in Notion |
| Build | Solo, with Claude Code |
| Cost | Runs on Notion's free plan and low-cost hosting |
The build
The CMS (Notion) — Notion is the content source. Two databases, one for case studies and one for articles; each page's properties become the site metadata, and the page body becomes the content. The site connects through an internal Notion integration: I share the databases with it, and it reads them with a secret key kept out of the code.
| Notion piece | How it's used |
|---|---|
| Projects database | One page per case study |
| Page properties | Company, role, timeline, platform, focus, cover |
| Page body | Headings, text, tables, images — rendered as-is |
Rendering (Claude Code + Next.js) — A renderer I built with Claude Code maps each Notion block to my own components, so the output always matches my design system.
| Layer | Approach | Tools |
|---|---|---|
| Framework | Server-rendered for speed and SEO | Next.js, TypeScript |
| Styling | Design tokens and utilities, no stray CSS | Tailwind CSS |
| Content | Fetched from Notion's API, cached and auto-refreshed | Notion API |
| Rendering | Each Notion block mapped to an on-brand component | Claude Code |
| Design system | Tokens → components → pages, consistent everywhere | Figma, Claude Code |
| Version control | History, branches, and a safety net for every change | Git, GitHub |
| Deployment | Builds and ships on every push to main | Vercel |
A living style guide — Every component also renders on a living style guide that imports the same code the site uses, so the design system can't quietly drift. I check changes there before they ship.
Protected case studies — Some of my best work is under NDA. Those sit behind a password: enter it once and the summary opens right there on the page — no separate login, no leaving the page.
Reflection
The goal was never to build a CMS. It was to make writing and shipping easy enough that I'd keep the site alive — and that part worked. Each piece pulls its weight: Notion for writing, Claude Code for building, a style guide to keep it honest.
- Owning the whole stack pays off. Design and code decisions feed each other, and nothing gets lost in a handoff.
- The speed of AI comes from the loop, not blind trust — describe it, read the diff, check it in the browser.
- A living style guide is what keeps everything consistent. Without it, small drifts quietly pile up.
- Cache Notion's images properly, so pages load faster and don't lean on links that expire.
