Skip to content
$ ash_
All work
2026/Designer & Developer/live

This Portfolio

98+ Lighthouse score across all pages

A content-driven portfolio built with Next.js 15, the App Router, and MDX. Dark-first, typography-led, 98+ Lighthouse.

Stack

Next.js 15React 19TypeScriptTailwind CSSFramer MotionMDXVercel

Problem

Most developer portfolios do one of two things: they over-engineer the stack to show off, or they use a template that looks like every other portfolio on the internet. I wanted something that felt genuinely mine — dev-forward without tipping into terminal-theme cosplay, and fast enough that the performance itself is part of the statement.

My Contribution

Designed and built the entire thing from scratch — design system, motion layer, content pipeline, contact API, and deployment. Every decision was deliberate:

  • Design system — single accent colour (electric lime), monospace typography throughout, 48px grid background as a subtle developer signature
  • Motion layer — custom cursor with spring tracking, clip-mask hero reveals, 3D perspective tilt on project cards, one-time intro screen; all respecting prefers-reduced-motion
  • Content pipeline — MDX files parsed at build time with gray-matter; adding a project is a git commit, not a CMS login
  • Contact API — Zod validation, honeypot field, in-memory rate limiter (5 req/IP/hour), Resend for email delivery

Architecture

Next.js 15 App Router, React Server Components by default. Client components are small islands — the nav, contact form, and the rotating hero word are the only things that need the browser. Everything else is statically generated.

The content layer is intentionally minimal: a tiny custom loader that reads .mdx files from disk at build time. Considered Contentlayer and a headless CMS; neither was worth the dependency weight for a one-person site.

Security headers are set in next.config.tsX-Frame-Options, Content-Security-Policy, and others applied to all routes. The contact API validates server-side and doesn't trust anything from the client.

Outcomes

98+ Lighthouse across all pages. Sub-2s LCP on a cold load. Achieving that required subsetting the monospace font to Latin, preloading only above-the-fold fonts, avoiding client JS on the homepage below the hero, and using next/font instead of Google Fonts CDN.

The codebase is also the primary technical signal in this portfolio — the decisions visible in the source are intentional.

Learnings

Deciding what NOT to build is as hard as what you do build. I cut a blog, an admin panel, and a Contentlayer integration before settling on the simplest thing that works. Constraint is a design tool.