loading...
The use case fits in a sentence: you just finished dinner, you want boba, you want to know what's open near you right now.
I built it for the San Gabriel Valley, the densest boba corridor in the US. The map worked on day one. The question underneath it did not: what counts as a boba shop, and is it open right now?
Google bills per call, so a public map on a metered API is a bill that scales with strangers' curiosity. Nothing about a shop is fetched from Google at runtime: the browser downloads one file and works out "open now" by itself.
That buys speed and a bill of zero. It costs freshness — and everything downstream is the answer to that.
Absence from a search never hides a shop. Removal takes an explicit closed status, failures get flagged for a human instead of acted on, and a wrongly-closed shop heals itself on the next pass.
The expensive way to answer this is to ask a model about every place the search returns. The cheap way is to notice that most places aren't ambiguous at all.
One shop was invisible for four months — and the pipeline found it on every single refresh and threw it away every time. A verdict from a rule that had since been deleted sat above Google's live label. Reordering the ladder fixed that shop and the whole class with it; hand-curated lists stay absolute, because curation is evidence and a cached verdict isn't.
Coverage had a second-order problem. Searches in the densest parts of the corridor come back full, which means the results are ranked rather than complete — a new shop with a handful of reviews is systematically unseeable. That's a ranking problem wearing a coverage problem's clothes.
Three times an obvious next feature got tested against data first, and three times the data said no:
Each no is written down with the numbers that made the call, so it stays decided.
Computing this in the browser is deceptively deep: shops open past midnight, holidays override the week, and the viewer's clock might be in Tokyo. All of it normalizes to one timezone and re-checks itself every minute, so an idle tab never shows "open" past closing.
The honest part: some of this was wrong for months, and I didn't find it by looking. A full-application audit did — it caught a filter that had an ARIA state, a default, and a spec entry, and was never actually read by anything; and removal logic running backwards, with shops sitting one refresh away from being deleted for no reason.
A brand-new shop with no listed hours can't render — there's nothing truthful to show. Limits like that are documented rather than papered over.
People search from memory, not from the sign above the door. They run a brand together as one word, leave the accent off, or type the shorthand their friends use. Every strictness in matching is one more way to show someone nothing while the shop sits right there on the map.
So matching is deliberately loose. Punctuation, spacing and accents are ignored, and a table of brand aliases maps what people actually type onto what the listing says.
Leniency has a cost, and the cost is a wrong match. So the collision risk got measured across the whole alias list before any of it shipped unguarded: only a small share changed which results came back at all, and just two reached across more than one brand — one a name two chains genuinely share, the other two unrelated shops. Cheap to check, and it's the difference between believing the leniency is safe and knowing it.
Every milestone build still boots from its commit, so the progression can be shown rather than described.
One rule, stated plainly: every surface is a cream, every ink is a brown, and there are no neutral grays anywhere — not even in the shadows. Round is the motif, with one piece of grammar holding it together: a status chip and an action button never share both silhouette and fill.
The system arrived the long way round: a visual audit that recorded only problems, then a Figma system, then a written spec, then a sweep through the code. Code is the canonical source of tokens and Figma mirrors it — deliberately, because the app is the artifact that ships.
The popup is the component that got argued down to the pixel, but it didn't start as a design problem. For months it just accumulated — an action here, a line of metadata there, each addition sensible on its own.
Only once the action row ran out of room did it earn a proper sitting: the same card drawn six ways at once, arguing the open questions side by side rather than one release at a time. Where the address goes. Whether the chip carries the closing time. Whether the actions share a row with the primary or get one of their own.
What settled it was structural rather than visual: the list row is the source of truth, and every other surface mirrors its anatomy.
Mobile started as a derivative of the desktop layout, and it showed. A wordmark header ate the top of the screen, a filter bar ran off the edge, a floating pill hid the list — and selecting a shop could put that shop on screen twice, once in a map popup and once in a detail sheet underneath it.
Four pieces became one bottom sheet with three heights — the map with search docked at the bottom, the list or a selected shop's card, and reading height. Selection always resolves to the same place whichever way you got there, so tapping a pin and tapping a list row end in the same view instead of two different ones.
The header went next, on both breakpoints. The wordmark shrank to a floating chip, then moved into an About dialog behind a hamburger, and the map controls dropped to the bottom to ride up with the sheet. Chrome kept losing to the map, which is the point: the map is the product.
Two smaller decisions run on the same logic. Hours adopted the pattern people already know from Google Maps — the week rotated to start today, today's row never moving as the other six expand below it — after the custom version was judged, correctly, as weird. And the sheet's resting height is measured against the tallest real shop card, so its edge never shifts as you move between shops.
This was AI-assisted at volume, with Claude Code as the primary implementer. The interesting part is the operating system around that: an instruction file that reads like an engineering process doc, a session log with an entry for every working session since day one, and persistent memory for the things a repo can't record — decisions already made, premises already tested and failed.
The habit underneath all of it: claims get checked by measurement, and the measurement gets written down. Data fixes are written as patterns rather than lookups, so the next shop matching the pattern gets the same treatment without a code change.
There's no end-to-end suite. UI verification lived in rigorous but disposable probe scripts, and the best of them deserve to be regression tests.
The site is live and still moving: every boba shop in California, open-closed status computed identically for a viewer anywhere, and a runtime bill that doesn't move when traffic does. Next ambition is coverage beyond the state.
What's deliberately missing is usage. There are no visitor numbers on this page, because the project was never instrumented that way — the honest outcome is the product itself.
What it demonstrates is the shape of the work: a product whose easy-looking surface sits on classification, freshness, and cost problems, and a process where premises get measured, audits get run, and the failures are documented next to the fixes.