Case studies, not a tech stack list.
Selected work with the problem, approach, and outcome behind it.
// loading projects...
Selected work with the problem, approach, and outcome behind it.
// loading projects...
This site. The only case study here you can verify yourself: read the source, run the tests, check the Lighthouse scores.
A personal site needs to double as evidence: SEO and AI-crawler discoverability, real Core Web Vitals, and a test suite and CI pipeline that actually catch regressions, without turning into a framework showcase that says nothing about how I actually build things.
Audit-driven: measure first (Lighthouse baseline, test coverage baseline), fix what the measurement actually showed, re-measure to confirm. Every fix that touched visible behavior was checked in a real production build, not just in dev mode, since dev-mode Lighthouse numbers are meaningless.
SEO and Best Practices both at 100 on every route, Accessibility at 100 (up from 94-96), CI green on lint/typecheck/41 unit tests/8 e2e tests/build. Performance improved unevenly across routes (see decisions below), reported as measured, not rounded up.
Context: Framer Motion's initial={{opacity: 0}} renders into the server HTML, so above-the-fold text was invisible until JS hydration ran the reveal animation: 1-2s of pure render delay, worst on the projects list at a 3.7s LCP.
Decision: Rebuilt the same fade+blur look as a CSS animation (tw-animate-css) for exactly the above-the-fold text that was affected, instead of cutting the animation to chase a better score. The entrance motion is part of the site's design, not incidental decoration.
Consequence: The projects list went from 70 to 89 performance. Other routes stayed roughly flat, because a correctly-timed CSS delay costs close to what the old render-blocking did. That trade-off was accepted deliberately rather than shipping a visually broken (blinking) animation for a bigger number.
Context: Bun was already the runtime and package manager; adding Vitest would mean a second JS toolchain just for tests.
Decision: Used bun test, which meant writing a small Bun plugin to parse MDX frontmatter for tests, since Bun has no MDX loader and dynamic import() of .mdx files needed a stand-in.
Consequence: One fewer dependency and a consistent toolchain, at the cost of about 60 lines of test infrastructure a bundler-aware runner would have handled for free.
Context: An early version of the sitemap test imported its expected route list from sitemap.ts itself, meaning the implementation silently dropping a route would never fail the test written to catch exactly that.
Decision: Rewrote weak tests to check against something independent of the implementation: the real App Router file structure on disk, the Math.ceil formula directly, raw frontmatter read straight from the MDX file, never a value re-imported from the module being tested.
Consequence: Caught real gaps during the rewrite (a pagination test that couldn't actually distinguish ceil from floor, an assertion that was trivially always true) that the original, weaker tests had missed.