Letting type move
We treat type as something that sits still — fixed in place, headings always the biggest thing, motion bolted on at the end. For one Creative Spark we dropped those rules and asked what type could do once it started moving. Creative Spark is our in-house R&D session: ninety minutes, a single theme, and a room full of people building prototypes with no client to please and nothing due at the end....
We treat type as something that sits still — fixed in place, headings always the biggest thing, motion bolted on at the end. For one Creative Spark we dropped those rules and asked what type could do...
The form we actually trust
In February 2026, we replaced our contact form with a submission pipeline we built and own. For the two years before that, it was a Pipedrive iframe inside our page. We had no control over fields, no client-side validation, no error handling beyond whatever Pipedrive showed inside its frame. When a submission failed, we found out from the client, not the system. A form we built, designed and...
In February 2026, we replaced our contact form with a submission pipeline we built and own. For the two years before that, it was a Pipedrive iframe inside our page. We had no control over fields, no...
We haven’t missed a Thursday in 28 weeks
We ship every week. Nobody outside the team could tell. The work was real (features, fixes, infrastructure changes) but invisible. Clients saw results on their pages. Visitors saw a polished site. Nobody saw the pace. We wanted a way to make the cadence obvious without turning it into a marketing exercise. The format we chose. Plain `.txt` files in a folder. That's it. `public/release-notes/`...
We ship every week. Nobody outside the team could tell. The work was real (features, fixes, infrastructure changes) but invisible. Clients saw results on their pages. Visitors saw a polished site....
One SCSS file that keeps 24 modules honest
Putting video back where it belongs
The problem with putting video in a git repo. The repo kept getting heavier, sprint after sprint. We were committing big MP4s next to the code, builds were slowing down, and deploys with them. Case studies lead with video, so every new client made it worse. We needed the media to live somewhere else, and to turn up in a size that suited whatever screen it landed on. But where? The CMS was out:...
The problem with putting video in a git repo. The repo kept getting heavier, sprint after sprint. We were committing big MP4s next to the code, builds were slowing down, and deploys with them. Case...
Twenty-four modules, one wrapper
Every case study was its own Astro page. That was the first problem. Each page had its own layout logic, its own spacing decisions, its own way of handling full-bleed sections versus contained text. Some used inline styles. Some used one-off SCSS files. None of them agreed on what "default spacing" meant. Adding a new case study meant copying an old one, deleting most of it and hoping the parts...
Every case study was its own Astro page. That was the first problem. Each page had its own layout logic, its own spacing decisions, its own way of handling full-bleed sections versus contained text....
Two months in Three.js, we started over
Two months into a Three.js prototype, the transition we wanted still wasn't there. We could see it in our heads and not on the screen. That was the moment we started over from scratch. Labs uses real-time shaders, physics-style interactions and transitions that happen at the GPU level. That behavior took roughly 6,000 lines of hand-written WebGL 2 and four months of iteration. We needed direct...
Two months into a Three.js prototype, the transition we wanted still wasn't there. We could see it in our heads and not on the screen. That was the moment we started over from scratch. Labs uses...
Five prototypes in 50 minutes
What a prompt system taught us about writing
The invisible translator
The cache that doubled as a coordinator
The rendering engine is what visitors see. The data layer feeds it, and keeping it stable under load was the less glamorous half of the build. This is the second post in the ON Labs series. The first covered the WebGL engine: shaders, draw calls, the GPU-level stuff. This one is about what happens before a single pixel renders: fetching project data from an external API, caching it and making...
The rendering engine is what visitors see. The data layer feeds it, and keeping it stable under load was the less glamorous half of the build. This is the second post in the ON Labs series. The first...
The testbed nobody will see
Every case study was a standalone Astro page with its own layout, its own component imports and its own way of breaking on mobile. Twenty-two of them. Each one built at a different time by a different combination of people, with whatever patterns felt right that week. Changing shared behavior (a hero transition, a spacing value, a navigation pattern) meant touching every file individually. The...
Every case study was a standalone Astro page with its own layout, its own component imports and its own way of breaking on mobile. Twenty-two of them. Each one built at a different time by a...
We test the revenue path
The contact form is the only page on this site that generates leads. So that's where the tests live. We don't chase coverage numbers. What we have is a targeted set of tests aimed at the things that would hurt if they broke: the forms, the pipeline that processes submissions and the CI that catches problems before production. Dependency injection as a testing technique. Our submission pipeline...
The contact form is the only page on this site that generates leads. So that's where the tests live. We don't chase coverage numbers. What we have is a targeted set of tests aimed at the things that...
Prototyping every direction at once
Prototypes need URLs, not GIFs
You’re at the end of our articles… for now!