This portfolio is a portfolio project in itself. It has 4 complete themes, motion effects, sound, easter eggs and a public debugger: a panel where anyone can turn every effect on, off or tweak it to see how the site works from the inside. Press ⇧D and try it.
This is a living post: it gets updated with every meaningful change to the site. The history is at the end, under Changelog.
Summary
- Stack: Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, GSAP 3 and Lenis.
- Deployment: Vercel, deploying automatically on every push to GitHub.
- Performance (mobile, PageSpeed Insights): 82 performance, 92 accessibility, 96 best practices and 100 SEO, before the fixes described below.
- Accessibility: respects
prefers-reduced-motion, has a lite mode on mobile and reached 100 on Lighthouse after 3 fixes. - AI: built pair-programming with Claude Code. I decided what and why; the AI implemented, measured and found bugs with me.
The idea
A developer portfolio needs to show two things: what you ship and how good you are technically. The site itself is the proof of the second.
The risk with an animation-heavy site is looking junior: lots of effects, little judgment. So each theme picks 2 or 3 signature effects and does them well, instead of turning everything on at once. And every effect can be turned off, because the visitor is in control of the experience.
The 4 themes
| Theme | Personality | Signature effects |
|---|---|---|
| Editorial | Design magazine: giant serif, paper and white space | Line-by-line text reveal, list with image on cursor, grain |
| Terminal | All mono, green phosphor | Text scramble, hidden terminal, scanlines |
| Aurora | Glass, shaders and light | Background shader, spotlight and tilt on cards, pinned horizontal scroll |
| Bauhaus | Swiss grid and primary shapes | Stacked cards, fast marquee, magnetic buttons |
Each theme has light and dark modes, its own fonts and even its own sound: paper for Editorial, 8-bit for Terminal, glass for Aurora and wood for Bauhaus.
Stack
| Layer | Choice | Why |
|---|---|---|
| Framework | Next.js 16 (App Router, Cache Components) | Pages generated at build time and served from the CDN; fast SEO and first paint |
| UI | React 19 + TypeScript | Typed components end to end |
| Styling | Tailwind CSS 4 + CSS tokens | Each theme is a set of variables; switching themes reloads nothing |
| Animation | GSAP 3 (ScrollTrigger, SplitText, ScrambleText) | Precise timelines, scroll-driven, with a controllable global speed |
| Scroll | Lenis | Smooth scroll without breaking position: sticky or native scrolling |
| State | Zustand | A single config (theme, effects, sound) saved in the browser |
| Sound | Web Audio API | Every sound is synthesized: zero audio files |
| Background | Plain WebGL | A light shader, no Three.js, rendered at low resolution |
| Deployment | Vercel | Deploy on push, preview per branch, global CDN |
Architecture in one sentence
A config (theme, mode, effects, layout, sound) lives in a store; every effect goes through one hook, useFx(), which combines that config with prefers-reduced-motion and the mobile lite mode before animating anything.
That gives three guarantees:
- No effect ignores accessibility: if the OS asks for reduced motion, nothing moves.
- Mobile gets a lighter version: cursor, magnetism, tilt, shader and smooth scroll are left out on small or touch screens.
- The debugger is just an interface for the config: each control changes one value, and each value has a button to reset it to the theme default.
Deployment
The code lives on GitHub and Vercel deploys on every push to main. Pages are generated at build time and served from the CDN; on the first measurement, the home page HTML was served straight from the edge cache.
The site is bilingual: /pt and /en. A proxy (Next 16's new name for middleware) reads the browser language: any variant of Portuguese goes to /pt, any other language goes to /en. If you pick a language in the switcher (the 文A icon), that choice sticks on future visits.
Performance: measured, not guessed
The first measurements were taken on the production site, right after the first deploy:
| Tool | Performance | Accessibility | Best practices | SEO |
|---|---|---|---|---|
| PageSpeed Insights, mobile | 82 | 92 | 96 | 100 |
| Local Lighthouse, desktop (median of 3) | 85 | 92 | 96 | 100 |
Lighthouse only measures loading. On an animated site, what also matters is smoothness while in use, so I wrote a test that opens the site in Chrome and scrolls to the end measuring every frame:
| Scenario | Average FPS | Slow frames (below 30 fps) |
|---|---|---|
| Desktop | 84 | 1.7% |
| Simulated phone, 4x slower CPU | 36 | 29% |
What those measurements changed:
- Preloader from 1.6 s to 0.4 s. It was holding back the LCP (the hero title) for over 3 seconds on mobile.
- Hydration error fixed. Blog dates were formatted in the server's time zone (UTC) and the browser's (UTC−3), and React saw different days. It only showed up in production, and Lighthouse is what flagged it.
- Next step: cut the initial JavaScript on mobile, where blocking time is still the biggest cost.
Accessibility
prefers-reduced-motionis respected by default, and the debugger can simulate it for testing.- Lite mode turns off the heavy effects on small and touch screens.
- Lighthouse flagged 3 issues, all fixed: contrast on a debugger shortcut,
aria-labelon paragraphs split by SplitText, and accessible names that didn't match the visible text. Result: 100 on accessibility.
How AI fit into the process
The site was built pair-programming with Claude Code, Anthropic's coding agent. The split was clear:
- I decided: the themes, what stays and what goes, making the debugger public, the tone of the copy, what's tacky (the "available for work" badge was cut for that reason).
- The AI implemented and verified: it wrote the code, ran build, lint and type-check at every step, opened the site in a browser to check it and measured performance.
- The most interesting bugs surfaced during that verification, not in the code itself (see below).
The lesson: AI speeds up execution a lot, but quality is still a decision. The judgment call, "is this good enough?", stays with whoever signs the work.
Bugs worth the trouble
- Gray shader. React's effect ran twice in dev (Strict Mode) and the cleanup destroyed the WebGL context of the same canvas. Fix: free the resources without killing the context.
- Frozen page on mobile. Lenis was created on the first render, destroyed when lite mode kicked in and then "stopped" on an already-destroyed instance, which locked scrolling. Fix: ignore instances that are no longer the current one.
- Leaking themes. The debugger's theme preview cards inherited rules from the page's active theme. Fix: theme rules that respect the nearest theme.
- Wrong dates. The time-zone hydration error described above.
What's next
- Real content: projects, case studies with numbers, testimonials.
- Less initial JavaScript on mobile.
- Real visitor data with Vercel Speed Insights.
Changelog
- Oct 16, 2026: English version with automatic language detection, SEO for search engines and AI, this post (with clickable badges for every tool mentioned).
- Oct 15, 2026: first deploy on Vercel, performance measurements, 0.4 s preloader, hydration fix, accessibility 100, debugger following the theme.
- Oct 15, 2026: synthesized sounds per theme, highlighted easter eggs, per-option reset, custom color picker.
- Oct 14, 2026: first version: 4 themes, effects, public debugger, ⌘K palette and hidden terminal.