jonathan hsu

jhsu.xyz

roleDesigner & Developer
teamSolo · Claude
timeline2026 — ongoing

jhsu.xyz was designed and built with Claude, from the first wireframe in Paper to the final commit.

View Paper artboard

Process

Wireframes Before Code

Designing a portfolio is a strange brief: the work has to be confident enough to have a point of view, but restrained enough that it doesn’t become the loudest thing in the room. Every decision (the orange, DaVinci on the display sizes only, a hero that is type rather than image, the discipline around animation) came from that tension: how do you make something opinionated without it upstaging what it’s supposed to showcase?

Every page was wireframed in Paper before a line of code was written. That upfront wireframing is what kept decisions intentional. Spacing systems, component hierarchy, content structure: all resolved in the canvas, so implementation had a clear brief to follow. The style guide came after shipping, working backwards from production to document what had already been applied, which also surfaced a handful of one-off styles that had quietly drifted from the system.

Outcome

Velocity and Direction

An early version of the composition’s hover tried to model a click as a spring. It was the obvious idea and it never worked: the impulse response peaks at the same moment however hard you push, so every attempt chose between a twitch and a flick. What ships instead takes its torque from the cursor’s actual travel, which reads as a strike because the hand really did the striking. Four attempts, and the useful part was learning that the excursion was the problem, not its shape. The same restraint governs everything that moves here, the composition assembling itself in the corner of the home page and the headline rising through its own highlighter, all of it kept quiet enough to serve the content.

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 FunCard’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 FunCard.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.

Working with Claude across the whole build (architecture, implementation, content, code review) reinforced something that applies to any collaborator: velocity and direction are different things. Claude was fast; I was responsible for what we were building toward. The skill was briefing, not prompting: specificity about intent, not just instructions.

Craft