I redesigned and upgraded my portfolio site recently, and the part worth writing about isn't the new stack — it's the order I did things in. I split the job cleanly in two. First I worked out the design in Claude. Then I handed that to Claude Code and let it do the building. Two tools, two jobs, and the handoff between them is where most of the value was.

Here's how it actually went.

Step one: figure out the design, in Claude

Before touching a line of code, I sat in a normal Claude conversation and treated it like a design partner. Not "write me a component" — more like "here's the site I have, here's the feeling I want, let's figure out what it should look like."

What that looked like in practice:

  • I described the site as it was (a Next.js App Router portfolio) and what felt dated about it.

  • We talked through layout, spacing, type, and motion as ideas first, in words, before any of it became markup.

  • I had Claude sketch concrete options I could react to, and I pushed back — "too busy," "I want the hero calmer," "keep the accent but tone it down."

  • Out of that I came away with a clear design direction I actually believed in, not a vibe I'd have to reverse-engineer later.

The point of doing this in Claude first is that design decisions are cheap to change when they're still words and mockups, and expensive once they're code wired into ten components. By the time I opened Claude Code, the hard calls were already made.

Step two: build it, in Claude Code

Then I switched to Claude Code, sitting right in the repo, and the conversation changed completely. Now it wasn't "what should this look like" — that was settled. It was "make the code match the design we agreed on, and bring the whole stack up to current while you're in there."

The upgrade it carried out:

  • Next 16 and React 19 — moved the app onto the current major versions.

  • Tailwind v4, CSS-first — this was the biggest structural change. The old tailwind.config.ts went away entirely; the design tokens now live in globals.css under @theme. That maps almost perfectly onto the design step, because the colors, spacing, and type scale we'd decided on became tokens in one file instead of being scattered through config and classes.

  • GSAP via useGSAP() — the animations moved off the old gsap.context() pattern onto the @gsap/react hook, which is the idiomatic way to do it now and plays nicely with React's lifecycle.

  • Zod 4 for the form validation, with the matching resolver bump.

  • A round of accessibility work alongside it — a skip link, visible focus states, and honoring prefers-reduced-motion so the animations back off for people who ask them to.

Because Claude Code was working in the repo, it could see the existing components, follow the patterns already there, and check its own work as it went. I wasn't pasting snippets back and forth. I was reviewing diffs in context.

One thing I deliberately did not upgrade

Worth calling out, because it's the kind of detail that bites you: I held lucide-react back on the last 0.x release instead of jumping to 1.0. lucide 1.0 dropped the GitHub and LinkedIn brand icons for trademark reasons, and I use both in the footer and a few other places. "Upgrade everything to latest" would have quietly broken those icons. So the stack is current except for that one pin, on purpose. Knowing why a thing is pinned is the difference between a careful upgrade and a reckless one.

Why the split worked

If I'd tried to do design and implementation in one conversation, they'd have fought each other. Design wants to wander, compare, throw things out. Building wants a fixed target and a tidy diff. Keeping them apart let each one be good at its job:

  • Claude held the open-ended part — taste, options, "what if."

  • Claude Code held the precise part — real files, the actual stack, work I could verify.

And the handoff was almost free, because both speak the same language. The design I'd agreed on in Claude translated directly into instructions Claude Code could act on. There was no lossy "now go interpret this Figma" step in the middle.

The result: a site that looks the way I decided it should, running on a current stack, with the accessibility basics covered — and I never had to hold the whole thing in my head at once. I made the design calls when it was time to make design calls, and the build calls when it was time to build. That's the workflow I'm keeping.