jhsu.xyz
jhsu.xyz was designed and built with Claude, from the first wireframe in Paper to the final commit.
View Paper artboardProcess
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.
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 }
animation: tooltip-in 130ms ease
animation: tooltip-in 130ms var(--ease-out)
ThemeToggle has no :active state
active:scale-95 + transition-[color,transform]
local SPRING = cubic-bezier(0.34, 1.56, 0.64, 1)
var(--ease-spring)
tilt fires on any pointerType
gate to e.pointerType === 'mouse'
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?
Gate the tilt to fine pointers, or leave touch as-is?
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.