Skip to main content
Journal

Engineering · Design

How we built this site

A walk-through of the stack, the design system, and the scroll theatre we built ourselves before we ship it for clients.

GoSmartR6 min read

We rebuilt this site from scratch in early 2026. Not because the old one was broken \u2014 it converted fine \u2014 but because we kept ending pitches with “you should see what we’re capable of” and pointing at someone else’s work. The site you’re reading is the answer to that.

It’s also the longest single project we’ve shipped for ourselves, and the one we learned the most from. This post documents the choices, the hard parts, and the opinionated bits we’d pick again.

The stack

We picked a stack that we already use on most client work, with one deliberate exception: we let ourselves over-engineer the interactions. The studio site is the place we test the patterns we eventually ship for clients.

Framework
Next.js 16 (App Router) · React 19
Language
TypeScript
Styling
Tailwind v4 · CSS variables for tokens
Animation
GSAP · Framer Motion · Three.js / R3F
Smooth scroll
Lenis
Auth + DB
Supabase
Payments
Stripe (Checkout + Customer Portal)
Email
Resend (transactional + Audiences)
Analytics
Plausible (cookieless)
Hosting
Vercel

The design system

Everything visual is driven by CSS custom properties, declared once in src/styles/globals.css under a Tailwind v4 @theme. That single source of truth made the “page mood” system possible: each route can tint the accent hue and the ambient gradients without touching a component.

Type

Three families: Syne for headings, Inter for body, JetBrains Mono for editorial labels and code. Sizes scale fluidly with clamp() from --text-xs through --text-display \u2014 no breakpoint juggling for type.

Color

A monochrome dark base anchored by one accent: lime #C8FF00. Used sparingly. Most of the page is foreground and background; the accent appears for status pills, focus rings, the FinalTheater underline, and very little else.

The theatre layer

The signature pattern across this site is scroll-driven theatre: long sections that pin to the viewport and scrub through 2\u20133 stages as you scroll.

The home page closes with the FinalTheater \u2014 a three-act pinned scene with the question, a live status panel (London time, bookings, latest launch), and the invitation. Case study pages have their own pinned testimonial moment and an editorial chrome (Vol. marker, ghost numerals, live URL pill). The result is that scrolling through the site feels like flipping through a magazine, not a marketing slide deck.

The site is a portfolio, but it should feel like a magazine.

— Working principle

The performance budget

Every interaction has a fallback. The hero ambient layer is a single CSS keyframe stack; the WebGL particle field is gated on hardware tier and reduced-motion. Lenis smooth scroll is wrapped so any visitor with prefers-reduced-motion: reduce gets native scroll. The point is that the “wow” moments never gate accessibility.

What’s next

The journal you’re reading is the next layer. Then a deeper /studio dashboard with live commit data, then probably a sandbox page where visitors can mess with the same primitives we used to build this. We’ll write about each as we ship them.

If you’ve made it this far and want a similar build, the scope calculator on /pricing will give you a quick range. Or just come and say hello.

Related reading

Next in the journal

Why we use Lenis (and when not to)

Smooth scroll is a budget item. After shipping a few sites with Lenis and a few without, here’s where we landed — and the test bug that bit us repeatedly.