← Back to blog

How I Overhauled My Portfolio

·5 min read

My portfolio was a two-column sidebar layout with shadcn/ui cards. It worked. It also looked like every other developer portfolio made with AI in 2025. Dark background, cyan accent, Inter font, card grid. A digital resume wearing a template skin.

I tore it down and rebuilt it. This is what happened.

Starting point

The old version had about 30 unused shadcn components, a barely visible geometric background, one font, and a footer that said "Vibe-coded in Argentina." It was functional but forgettable. If you showed it to someone and said "AI made this," they'd believe you instantly.

The first redesign

I switched to a single-column editorial scroll. Playfair Display for headings, Inter for body. Added a cursor spotlight effect in the hero (the Brittany Chiang move), scroll-driven timeline animation for experience, and section-by-section reveal animations with Motion.

It looked better but still felt template-y. The critique framework I was using flagged specific tells: gradient text on the name, uppercase tracking-widest subtitle, numbered section labels, a decorative gradient line. All textbook AI portfolio aesthetics.

Killing the AI tells

This was the most useful part. I ran the site against Wikipedia's "Signs of AI writing" checklist and my own UI/UX rubrics. Each round found something:

Every removal made the design better. Fewer elements, more confidence.

What I added instead

After stripping everything down, the site felt too empty. I needed to put something back, but not decoration. Interaction.

Reactive dot grid

The background dot grid isn't static. It's a canvas that renders dots at the same 40px intervals, but they repulse from your cursor with spring physics and settle back. The whole page background is alive.

Getting this right took a while. First I tried a WebGL shader (flowing gradient mesh that follows the cursor). It looked like a cloud following you around. Didn't fit at all. The reactive dots work because they're already part of the visual language. They don't add a new element, they make an existing one interactive.

Character scatter

Hover over my name and individual letters lift from the cursor position. Spring-animated with Motion's useSpring. The kind of thing you notice by accident.

The core of it is a distance calculation per character:

const charPos = index / total;
const dist = Math.abs(mouseX - charPos);
const influence = Math.max(0, 1 - dist * 6);
y.set(-influence * 12);

Each letter checks how far it is from the cursor, and lifts proportionally. The spring handles the bounce-back.

Magnetic icons

Social links and the resume button pull toward your cursor. Stiffness 200, damping 20. You might not notice it consciously, but the UI feels physical. I replaced the CSS hover:scale-110 transforms with spring physics. Different feel entirely.

GitHub contribution grid

The hero has my actual GitHub contribution data rendered as squares with a vignette fade. Fetched at build time via GraphQL, revalidated daily.

The tricky part was alignment. The squares need to sit on the same 40px grid as the background dots. I went through several iterations: SVG with preserveAspectRatio (squares got stretched), CSS mask (didn't work), solid color overlay (blocked the cursor spotlight), per-square opacity fade with distance calculation (worked).

The fade uses a quadratic curve so the falloff is smooth, not a sharp ellipse:

const dist = Math.sqrt(dx * dx + dy * dy);
const fade = Math.min(1, Math.max(0, (dist - 0.5) / 0.8)) ** 2;
const opacity = baseOpacity * fade;

Two-color system

Everything was cyan for a long time. Then I added warm amber for action elements: the resume button, the email CTA, the availability dot, the "In Progress" badge. Cyan stayed for identity and information. Your eye goes to the amber because it's scarce.

The resume wording problem

After finishing the design, I realized my resume text read like ChatGPT wrote it. Every bullet started with an action verb. Every bullet was the same length. Every bullet justified itself with an "enabling" or "establishing" clause. I checked Wikipedia's "Signs of AI writing" article and found my resume hit most of the tells.

The fix: fragments, varied lengths, colons instead of em dashes, some bullets that are just tech lists with no narrative. "Gamification backend: achievement APIs, streak calculations, leaderboard cron jobs. NestJS, PostgreSQL, SQS/SNS" reads like notes a human jotted down. The old version ("Built gamification backend features including achievement tracking APIs, streak calculations, and leaderboard notification cron jobs using NestJS, PostgreSQL, and AWS event-driven services") reads like a prompt output.

What I'd do differently

I spent too long on visual polish and not enough on content. The interactions are cool but a recruiter spends 10 seconds on the page. The bio and experience bullets matter more than whether the dots repulse from the cursor.

I also went through too many font changes. Instrument Serif, then Playfair Display, then JetBrains Mono, then Space Grotesk. Should have picked one early and committed.

The best decision was the critique loop. Running the site against a rubric after each change, getting honest feedback about what still looked AI-generated, and fixing each issue concretely. Without that, I'd still be adding features instead of removing tells.

The portfolio is done. Time to build things worth putting on it.

← Back to blog