jonathan hsu

jhsu.xyz

roleDesigner & Developer
teamSolo · Claude
timeline2026 — ongoing

In typical designer fashion, I constantly have the urge to redesign my portfolio, but I never get around to it. The last time I updated my website was before agents, so this became the chance to do two things at once: rebuild my portfolio, and find out where Claude held up and where it didn’t. It was one of my first real collaborations with an agent, so I used it to answer one question: where do agents fit into my design process?

Process

How I approached the portfolio redesign

I approached the redesign the way I’d approach a real product feature, which meant doing the parts I usually skip on personal work: who it’s for, its purpose, and its constraints. Most of the early effort went into deciding which qualities I wanted a visitor to actually notice, an idea I took from Josh Puckett’s writing on Interface Craft. I landed on three facets:

  • Intentional: every animation, interaction and styling property should feel fine-tuned.
  • Modular: the same body of work should take a different shape on each page and still feel cohesive.
  • Playful: the site should feel physical to use and carry some of my personality, with some restraint.

Then I gathered references and built a moodboard for the direction I wanted. All of it went to Claude as a single brief, along with the facets, the constraints, and enough about me and the work that what came back was specific to me.

I first explored layouts and site structure in Figma, but I quickly felt that Claude’s output through the Figma MCP wasn’t good enough to design against, and I spent longer working around that than I should have before switching. Moving to Paper fixed it. The artifacts improved immediately, and once implementation started, Claude turned out to be better at building from a Paper file than from a Figma one. Most of my design work happens in Paper now.

Outcome

Obsessing over details

I wanted the portfolio to feel intentionally crafted and still be easy to read. My main concern was striking a balance between the two. Anything opinionated had to hold up on every page without pulling attention off the work itself, so I kept it to a handful of details rather than a few big gestures. Three of those took most of the time.

1. The ruler

I wanted the portfolio to play into the drafting aesthetic of real-world instruments. Cutting mats. Rulers. Rough sketches. The subtle grid lines on every page serve as visual references to accentuate content alignment. I wanted that structure to be reactive so that it didn’t feel like a static SVG.

2. The mosaic

The mosaic on the home page renders differently on every visit. I defined a set of shapes and had them placed randomly across a grid of cells. This approach got me pretty close to the result I wanted, but the composition still felt “off.” The shapes felt too cramped. For the composition to hold together, I had to introduce some logic for how shapes were grouped.

I cut out shapes that felt visually unaligned with the rest of the composition. I also introduced weighting that determined how shapes were placed alongside each other. This created a better-balanced composition of curved and straight-edged shapes.

edge weighting1.9
The heavier the weight, the harder each shape tries to match the edges of whatever it’s sitting next to.

3. The cutting mat

The work deck intro card was the result of an experiment where I replicated a cutting mat. It started off pretty basic, but that felt too generic, like something an agent would produce. The protractor added to the drafting aesthetic of the card, and provided some curvature to an otherwise edge-heavy piece. Then I added assets from my previous work to make it feel personalized.

The solid fill coloring on the cutting mat felt out of place and unnatural. I landed on a halftone CMYK shader that gave the card some texture. I liked the state of the card, but felt like there was still something missing: the roughness that comes from sketching. That led to rough.js, a library I found that emulates the feel of drawing by hand.

Reflection

What I learned

The split between what I handed over and what I kept shifted a lot during this project. Here’s where it landed.

1. Working from a reference

Claude works great when there’s already something to work from. I gave it the Paper wireframes and the first version came back close to them. It’s quick, too. Getting from an idea to a wireframe or a working prototype got cheap, so I could push a direction far enough to judge it rather than talking myself out of it first.

2. Precision without judgment

It’s good at anything with a right answer. Precise offsets, consistent spacing, the same value applied everywhere it appears. It can’t tell you whether the result feels right, and that turns out to be most of the work.

Animation is the clearest case. Claude implements the timing you ask for accurately, as long as you use the right words for what you want. It just has no opinion on whether that timing is the right one. Placement is the same. It’ll put an element exactly where I say, and I still have to nudge it around before it sits properly.

When I let it make those calls itself, the work came back generic, with obvious tells that nobody had chosen anything.

3. Memory between sessions

Some of what I learned through this project had nothing to do with design. One of the first hurdles I encountered was managing context for Claude. On a project this long, I found myself explaining decisions I’d already made, and reopening ones I thought were closed. I dedicated effort to configuring proper workflows for Claude Code. Writing decisions down where the next session reads them first. Clearing context before it fills up with things that don’t apply anymore. I built a small skill, /save, that closes a session by committing the work and writing down what happened.

4. Skills as a second pass

Partway through I started running other people’s skills over my own interaction code, Emil Kowalski’s and Interface Craft’s. They caught things I didn’t know to look for, and a few I’d have noticed eventually and let slide. I treated it like a review, so it only helped once I had something finished to look at.

Claudewhat building this actually looked like

run emil-design-eng over the v12 motion and interaction code

Ran skill /emil-design-eng

Most of it holds up. The deck’s damped scroll, the card spring-return, the page entrance and the reduced-motion handling are all on-principle. The real findings cluster in the tooltip I just built and in the theme toggle.

Five changes worth making:

.tooltip-content animates on every open

[data-state='instant-open'] { animation: none }

Once one tooltip is open, adjacent ones should appear instantly. The provider already skips the delay, so the animation should skip too.

animation: tooltip-in 130ms ease

animation: tooltip-in 130ms var(--ease-out)

An entrance wants a strong custom ease-out. The built-in ease is too weak, and the token already exists.

ThemeToggle has no :active state

active:scale-95 + transition-[color,transform]

Pressable things should answer the press. Scaling to 0.95 confirms the interface heard the click.

local SPRING = cubic-bezier(0.34, 1.56, 0.64, 1)

var(--ease-spring)

The same curve now lives in the token set, so the local constant is a second copy that can drift.

tilt fires on any pointerType

gate to e.pointerType === 'mouse'

Touch triggers a false-positive tilt on tap. Hover-like effects belong to fine pointers.

anything you looked at and decided to leave

Two, and I want them on the record rather than quietly skipped.

  • Tooltip slides directionally, rather than scaling from its origin. The principle behind origin-aware scaling is spatial connection to the trigger, and a 3px translate toward it already achieves that. Deliberate, so it stays.
  • The card animates `box-shadow`, a paint rather than a transform. A real cost, but subtle and only while tracking, and rewriting it to a pseudo-element opacity fade is more churn than the frames are worth today. Noted, not fixed.

apply the five, leave the two

Fold SongShelf’s local spring into var(--ease-spring), or leave the duplicate curve?

Fold into the token

Gate the tilt to fine pointers, or leave touch as-is?

Gate it

Edited globals.css+6-2

Edited ThemeToggle.tsx+1-1

Edited SongShelf.tsx+5-2

Built after the fixes

Clean. The five are in, and the two I left are written down where they will be found again rather than rediscovered.

So where do agents fit? Anywhere there’s already something to follow. Claude built more of this site than I expected it to, and it moved fastest whenever I’d already decided what I wanted. A lot of the design still needed my judgment: whether a timing feels right, whether a composition is balanced, whether something reads as considered or just generated. That stayed mine.

View Paper artboardCraft