Skip to main content
4 min readCalvin Ku

Launch Notes β€” Building a Portfolio That Ships Fast

Reflections on designing and building a new portfolio with ChatGPT-inspired UI, aggressive performance budgets, and type-safe content.

  • #meta
  • #design
  • #performance
  • #launch
Launch Notes β€” Building a Portfolio That Ships Fast

After months of iteration, the new portfolio is live. This post documents the thinking behind key decisions, the tech stack, and early performance wins.

The Problem With My Old Site

My previous portfolio had three major issues:

  1. Generic design β€” Looked like every other developer portfolio with the same templates
  2. Slow performance β€” Initial bundle was 500KB+, LCP regularly exceeded 3s on 4G
  3. Hard to update β€” Static HTML meant manually editing files for every project

Result: Low engagement, high bounce rates, and I dreaded adding new work.

Design Goals

I wanted something that felt familiar yet distinctive. After spending so much time in ChatGPT's interface, I realized its UI patterns solve exactly the problems I had:

Borrowed from ChatGPT

  • Left sidebar navigation β€” Clear hierarchy, always accessible
  • Large center canvas β€” Content gets the space it deserves
  • Subtle right rail β€” Featured items without distraction
  • System fonts β€” Instant loading, no font flash

What I Added

  • Warm minimalism β€” Soft borders, subtle shadows, rounded corners
  • Typography-first β€” Big headlines (48-64px), generous line-height
  • Dark/light themes β€” High contrast, WCAG AA compliant

The goal was "ChatGPT for portfolios" β€” familiar chrome with personality.

Performance Budget

I set aggressive targets from day one:

const perfBudget = {
  lcp: 1000,        // 1s on 4G
  fid: 100,         // < 100ms
  cls: 0.05,        // Nearly zero shift
  jsBundle: 120000, // 120KB gzipped
};

Why so strict? Fast sites:

  • Rank better in search
  • Get shared more on social
  • Convert higher on CTAs
  • Feel more polished

How We Hit the Targets

1. Static Generation

Every page is pre-rendered at build time. No client-side data fetching, no loading spinners:

Route (app)                    Size     First Load JS
β”Œ β—‹ /                       5.44 kB         107 kB
β”œ β—‹ /work                   3.21 kB         105 kB
β”” β—‹ /blog                   3.45 kB         105 kB

2. Type-Safe Content

Contentlayer parses MDX at build time and generates TypeScript types. Invalid frontmatter fails the build, not production:

import { allProjects } from '@/.contentlayer/generated';
 
// βœ… Fully typed
allProjects.forEach(project => {
  console.log(project.slug);  // string
  console.log(project.url);   // string
  console.log(project.status); // 'planned' | 'shipped' | 'archived'
});

3. Smart Image Optimization

Next.js Image component handles:

  • Automatic WebP/AVIF conversion
  • Responsive srcsets
  • BlurDataURL placeholders
  • Lazy loading below the fold

4. Minimal JavaScript

No heavy animation libraries, no unnecessary client-side routing. Just:

  • Theme toggle (dark/light)
  • Copy email button
  • Search dialog (Cmd+K)

Total JS on landing: 92KB gzipped (23% under budget)

Tech Stack

Core

  • Next.js 15 (App Router) β€” Static generation, image optimization, font subsetting
  • TypeScript (strict mode) β€” Catch errors at build time
  • Tailwind CSS 4 β€” Utility-first styling, minimal CSS output

Content

  • Contentlayer β€” Type-safe MDX with frontmatter validation
  • MDX β€” Write rich content with React components
  • rehype-pretty-code β€” Beautiful syntax highlighting
  • reading-time β€” Automatic reading estimates

Infrastructure

  • Vercel β€” Zero-config deployment, preview branches
  • Plausible β€” Privacy-friendly analytics
  • GitHub Actions β€” Lighthouse CI on every PR

Early Results

Performance (Lighthouse, Desktop):

  • Performance: 100/100
  • Accessibility: 100/100
  • Best Practices: 98/100
  • SEO: 100/100

Core Web Vitals (4G, throttled):

  • LCP: 0.6s (40% under target)
  • FID: 12ms
  • CLS: 0.01

Developer Experience:

Time to add a new project:

  • Before: 45min (HTML editing, image optimization, manual deployment)
  • After: 5min (drop MDX file in content/work/, push to git)

Lessons Learned

1. Familiar Beats Novel

Users don't need to learn your navigation if it looks like something they already use. The ChatGPT UI patterns were instantly recognizable.

2. Type-Safety Pays Off

Contentlayer caught 12+ frontmatter bugs during development. Each would have been a silent production issue.

3. Performance Compounds

Fast sites get shared more β†’ rank better β†’ get more traffic β†’ convert higher. The 83% LCP improvement wasn't just a vanity metric β€” bounce rate dropped 22%.

4. Constraints Drive Creativity

The 120KB JS budget forced me to eliminate unnecessary dependencies. The site feels faster and simpler.

What's Next

Short term:

  • Add 2 more case studies
  • Publish 10 blog posts (you're reading #1!)
  • Integrate Calendly for "Book a call" CTA

Longer term:

  • i18n support (EN/δΈ­ζ–‡)
  • Newsletter integration (Buttondown)
  • "Notes" micro-blog on right rail

Try It Yourself

The site is open source. Check out the repo to see:

  • Full Contentlayer config
  • MDX remark/rehype setup
  • Dynamic OG image generation
  • Lighthouse CI workflow

Thanks for reading! If you're building something similar, feel free to reach out β€” I'd love to hear what you're working on.