Skip to contact form
← BlogDesignFebruary 14, 2027 · 3 min read

We deleted the empty box

No stock photos, ever — but the labeled placeholder we replaced them with turned out to be its own kind of noise. Now a screenshot that doesn't exist renders nothing at all.

very case study on this site has a slot for real product screenshots — and for work that's still in progress, some of those slots are genuinely empty, because the screen doesn't exist to photograph yet. The easy fix is a stock photo: someone smiling at a laptop, a vague dashboard mockup that looks plausible from six feet away. It fills the space. It also isn't the product, and pretending otherwise for the sake of a tidier-looking page is exactly the kind of small dishonesty that compounds.

So for a while we did the obvious honest thing instead: a dashed frame, the shot's intended caption in mono type, and a small note reading "add src to lib/case-studies.ts" — a reminder to ourselves about where the file goes. It was truthful. It was also, we eventually admitted, a developer's TODO list rendered at full width on a page meant for clients, three times per case study. Truthful and worth showing are not the same test.

Astockphototellsasmallliethesizeoftheboxitfills.Alabeledplaceholdertellsthetruth,atthesamesizeandthesizewastheproblem.

All three, side by side

the lie

generic stock photo of “people looking at a laptop, smiling”

what we used to do

trail map with elevation profile

add src to lib/case-studies.ts

what we render now

nothing at all

What ships today is the third card. CaseStudyPage filters the shot list down to entries that have a src before it renders anything, and the whole "Screens" section is behind a check on what's left — so a study with no photographed screens yet doesn't show an empty gallery, a placeholder, or a heading promising screens that aren't there. It shows the writing, and stops. ShotFrame itself no longer has a fallback branch at all; by the time a shot reaches it, the image is guaranteed to exist.

The data stays, the UI goes

The filtering happens at render, not in the content file. Every shot still carries its alt text and caption in lib/case-studies.ts, written before the screenshot exists — that's the note-to-self the dashed frame used to be, kept where notes to ourselves belong. Adding one src brings that shot back, and the section reappears with it. Nothing needs rewriting to publish; the page is already waiting for the file.

What we actually learned

The first version optimised for not lying, which is the right thing to optimise for and not the only thing. An unfinished page is allowed to be shorter than a finished one — that's what unfinished looks like, and padding it with an honest label is still padding. The discipline that survived isn't the component, which is now a few lines shorter than when it had a fallback. It's the rule underneath: when there's nothing to show, show nothing, and don't reach for something to put in the gap.

Want this kind of thinking on your project?

This is how we work through real decisions. If you're weighing a build of your own, tell us about it — we reply within 24 hours.