About

A front-end practice built around content, contrast, and clarity.

I design and build the front of small products: marketing sites that a client can actually edit, booking flows that survive contact with a busy weekend, and the design systems that keep the rest of a team moving in the same direction. Most of that work is Next.js and React. This site is deliberately not, because the questions worth answering on a project sit above the framework and travel with you when it changes.

How I work

  • Contrast is a build constraint

    Every text and interface pairing on a project of mine is checked against WCAG AA before it ships, in both themes. It is cheaper to pick a passing colour than to relight a finished screen, and nobody has ever asked me to make the type harder to read.

  • Motion has to be optional

    Animation earns its place by explaining a change of state. Anything that only decorates gets cut, and everything that survives stands down completely when the operating system asks it to.

  • The content model comes first

    A typed schema catches a missing field at build time instead of rendering an empty heading in production. Getting the shape of the content right is most of the work on a marketing site, and the layout follows from it.

  • Write the reasoning down

    Patterns, decisions, and the constraints behind them go into the repository next to the code. That is what makes a system outlive the person who built it, and it is the difference between a component library and a working practice.

Colophon

Nuxt 4 and Vue 3, with Nuxt Content holding the case studies as typed Markdown so a missing field fails the build rather than rendering a blank heading. Images run through Nuxt Image, which serves AVIF in production and IPX in development. Type is Inter, self hosted and subset by Nuxt Fonts so nothing shifts on load.

Styling is plain CSS: custom properties, cascade layers, and logical properties, with no utility framework anywhere in the build. Navigating from a card on the index page to its case study runs a real View Transitions morph in Chrome, and degrades to an ordinary navigation elsewhere without any visual break.

Screenshots and scroll captures are produced by a Playwright script in this repository and committed alongside the content. Nothing on the page reaches out to a client site at render time, so a redesign or an expired domain on their end never leaves a hole on mine.