Available for new projects
Tell me about your brand, your product or your next campaign. I review every inquiry personally and reply with a clear plan within 48 hours.
Colophon
This site is the longest example of what I do: I designed it and built it through to production. Here are the decisions and, more to the point, why each one was made. It shows how I think before we talk.
Next.js on the App Router, with the database as the source of truth.
Next.js 16, TypeScript, Tailwind and Supabase for data, auth and files. Almost everything you read here comes from a custom admin panel rather than the code: projects, posts, testimonials, tool lists and most of the copy. Changing a word shouldn't require a deploy.
One variable family instead of a catalogue.
Geist carries the whole system through its continuous axis: intermediate weights for headings, 200 reserved for figures, the variable italic for accents, and the mono for labels. Archivo handles body copy. Pairing two competing families is easier than getting a voice out of one.
A brand pair and a single saturated colour.
Paper and ink as the pair, red as the only accent. That red has two separate tokens, one for small text on light and one for text on dark, because the same value doesn't clear minimum contrast in both places. A colour you can't read isn't a brand colour, it's decoration.
Entrances run on the compositor, not the main thread.
Reveals are CSS animations triggered by an IntersectionObserver. They used to be computed frame by frame in JavaScript, which pinned them to 60fps on iOS even on 120Hz displays. None of it moves if the system asks for reduced motion.
Having a session and being an admin are different things.
Supabase's signup endpoint answers to a public key, so anyone can create an account: write policies that trust «authenticated» would be wide open. Here they ask a function living in a schema the API doesn't expose, and the admin list can't be read from outside.
Every URL serves stable content, no guessing.
The site never redirects based on browser language or location: switching is explicit. Guessing leaves Googlebot, which crawls from the US, seeing the homepage as a redirect, and leaves visitors sharing links that open in a different language for someone else.
The rendered page gets audited, not the source.
Every test run opens the real pages in a browser and puts them through axe-core at mobile and desktop sizes. The first pass found two serious failures that the build, the typecheck and the tests all let through, because none of those look at the page.
Publishing is a push, and search engines find out on their own.
Production ships from the main branch on Vercel. Publishing a project pings search engines via IndexNow and regenerates only the affected pages. The sitemap is built from the database along with its language alternates, so there's no hand-maintained list of URLs.
If you're evaluating someone who designs and also delivers, this is the sample. What's here is what I bring to a client's product.