Plate № 24 · UX · maps / location
A pattern from the gf.cx specimen book
The good-map pattern
"its a micro-pattern for maps" · "aerial, close-up hover, street-view" · "graduate it to the assets.gf.cx shelf".
The problem
You're writing a page that names a real place — a data centre, an office, a trailhead — and you want to show it, not just link a map. The reader has three questions, in order: what does it look like from above? where exactly is the building? what's it like at street level? Answer them ad-hoc and every dossier drifts: one starts wide and never zooms in, one buries the building in a district shot, one bolts on a heavy Google embed that janks the whole page on load.
The behaviour that answers those three questions is identical every time; only the bugs differ — the same drift the shared modal and provenance footer kill one layer up. The fix here is a composition: three small primitives that snap together into one canonical sequence, lifted to a single CDN-hosted css+js.
The three beats
A location dossier is three figures, each answering the reader's next question:
- 1 · Aerial — a
.rollimgmouse-over reveal. For maps, reverse the generic default: start on the close-up (the pinned hall) and hover out to the wider district. The subject is the facility, so land on it; the hover answers "where is this?" - 2 · Close-up hover — the reveal frame of beat 1. Because both frames share the same box, reversing direction is a pure image swap.
- 3 · Street-view — a live Google embed, either deferred (
.svfacade, click-to-load) or always-on (.embed3d). Frame it at the building's 3/4 edge — corner-on, two faces receding, not a flat head-on facade — the classic architectural three-quarter view that reads the massing and gives a long windowless hall depth. The street-view sibling of the aerial's exact-size rule.
The element, in-page
The live specimen below runs in a sandboxed iframe (the pattern-preview primitive). Hover the aerial — it starts on the pinned block and zooms out to the district. The hover image is the exact pixel size of the base, so the overlay lands perfectly; no object-fit guesswork.
hover · zoom out
How you use it
Link the shared css once (add the js only if you use .svfacade). Then compose the three beats — the aerial needs no script at all:
<link rel="stylesheet" href="https://assets.gf.cx/map-dossier/map-dossier.css">
<!-- only if you use .svfacade (click-to-load) -->
<script src="https://assets.gf.cx/map-dossier/map-dossier.js" defer></script>
<!-- Beat 1+2 · aerial: base = close-up, hover = wider (reversed for maps) -->
<div class="rollimg" tabindex="0">
<img src="…/hall-closeup.jpg" alt="The pinned hall, close">
<img class="rollup" src="…/district-wide.jpg" alt="Wider context">
<span class="rollhint">hover · zoom out</span>
</div>
<!-- Beat 3, always-on · the embed IS the point -->
<div class="embed3d" style="max-width:520px;aspect-ratio:16/10">
<iframe src="https://www.google.com/maps/embed?pb=…" loading="lazy"
allowfullscreen allow="accelerometer; gyroscope; fullscreen"
referrerpolicy="strict-origin-when-cross-origin"
title="Interactive Street View — …"></iframe>
</div>
<!-- Beat 3, deferred · loads Google only on click -->
<div class="rollimg svfacade" data-embed="…embed?pb=…"
data-embed-title="Interactive 3D aerial — …">
<img src="…/still.jpg" alt="Ground still">
<span class="rollhint">click for live view ↗</span>
</div>
The one rule that makes it clean: the .rollup image must be the exact pixel dimensions of the base (e.g. both 1000×625). Then the inset:0 overlay is pixel-perfect and reversing the direction — tight↔wide — is just swapping which src is the base and which is the .rollup. No re-crop.
An optional fourth beat — the place, dreamt
The three beats locate a facility; a fourth can colour it. The reveal mechanic doesn't care what the .rollup overlay is — only that it's the base's exact pixel twin — so the overlay needn't be another photograph. Run the real Street-View still through an image-to-image model and the hover dissolves the photo into a hand-painted Studio-Ghibli cel of the same building: base = what's there, hover = how it dreams. Identical primitive, identical exact-size rule; only the overlay's provenance changes.
Hover the frame below — the Escher-Wyss block at Josefstrasse 225 (Equinix ZH4) melts from Google Street View into a painted daydream of itself.
hover · dreamt
The overlay is one image-to-image call per frame — fal.ai FLUX.1 Kontext [max], which restyles the picture while holding the building's geometry (~$0.08 a frame):
# submit · queue API, header is "Authorization: Key $FAL_KEY" (not Bearer)
POST https://queue.fal.run/fal-ai/flux-pro/kontext/max
{
"image_url": "https://media.gf.cx/srvc/street/…-streetview.jpg",
"prompt": "Convert this photograph into a Studio Ghibli hand-painted flat
cel-animation frame … preserve the building's exact architecture,
proportions and composition. No photorealism, no 3D render."
}
# poll …/requests/{id}/status → GET the result → resize to the base's EXACT px
Two rules keep it honest. The base stays the real photograph — the dream is the reveal, never the claim; a locator that asserts a painted building has stopped locating. And the credit rides with the base: Street View © Google. The reusable generator is fal_ghibli.py — env-or-1Password key, the queue-REST pattern and the exact-size resize baked in; point it at any grab and it returns the matched overlay.
The two duality axes
Every dossier makes two independent choices:
- Axis A — which way the aerial hover runs. The generic default is base-wide, hover-detail (e.g. a street facade). Maps invert it: the interesting subject is the facility, so start tight and hover out. Hint chip reads "hover · zoom out".
- Axis B — deferred vs always-on street-view.
.svfacadedefers a heavy or optional third-party embed until the reader clicks (page stays fast, ships no 3P until intent — e.g. a weighty Google 3D aerial)..embed3dputs the embed live on load when it is the point (e.g. a Street View pano).
Where it applies
- Any dossier that names a real place. Data-centre teardowns, office locators, trailheads, venue pages — anywhere the reader benefits from aerial → close-up → ground. Origin: srvc.gf.cx/transit, where the Zürich, Dallas and Luxembourg dossiers each locate a carrier hotel this way.
- Pick beats à la carte. The three primitives are independent — a page can run just the reversed-hover aerial, or just an always-on Street View, without the others.
- Performance-sensitive pages. Reach for
.svfacadewhen a live Google embed would otherwise block or jank first paint; the reader pays the third-party cost only on click. - Skip the reversed hover when there's no meaningful "wider context" to reveal (a single isolated building on open land), and skip the embed entirely where a plain map link is enough.
Reusable elements
The named, copyable pieces — lift any one without the others:
- Shared asset
assets.gf.cx/map-dossier/map-dossier.css+map-dossier.js— the three primitives themed off local CSS vars (hard fallbacks for--line/--bg-warm/--mono). The js is self-initializing, singleton-guarded, and observes the DOM;.rollimgand.embed3dneed no js at all. .rollimgmouse-over reveal — base<img>sets the box,<img class="rollup">fades in on:hover/:focus-within, a.rollhintchip fades out. Pure CSS. Exact-size overlay = pixel-perfect, reversible by swapping src..svfacadeclick-to-load facade —data-embed="…"(+ optionaldata-embed-title); the js injects the live<iframe>on first click / Enter / Space, so Google loads only on intent. Auto-getsrole="button"+tabindex="0"..embed3dinline embed box — a framed 16:10 wrapper for an always-on<iframe>or fixed<img>; per-instance sizing via inlinemax-width/aspect-ratio. Pure CSS.- The exact-size overlay rule — the load-bearing constraint that makes reveal and direction-reversal trivial: crop the hover source to the base's aspect, resize to identical px.
- The "dreamt" overlay (optional) — a
.rollupthat isn't a second photo but a Studio-Ghibli image-to-image restyle of the base (fal.ai FLUX.1 Kontext [max], ~$0.08/frame, resized to the base's exact px). Generator:fal_ghibli.py. Decorative, not locational — the base photograph stays the source of truth and keeps the Google credit.
Reference
- Source
- Dan, 2026-07-12 — "its a micro-pattern for maps" · "aerial, close-up hover, street-view" · "graduate it to the assets.gf.cx shelf". Distilled from the bespoke transit visuals (2026-07-09 → 07-12), then lifted to the shelf 2026-07-13.
- In use / example
- srvc.gf.cx/transit — the Zürich, Dallas and Luxembourg dossiers each locate a carrier hotel with all three beats. Shelf source:
assets.gf.cx/map-dossier/. - Reusable elements
- Shared CDN css+js ·
.rollimgreveal ·.svfacadefacade ·.embed3dbox · the exact-size overlay rule (listed above). - Origin
- Bespoke
.rollimg/.svfacade/.embed3dhand-wired intosrvc.gf.cx/transit; the reversed-hover "micro-pattern for maps" and the facade↔direct-embed duality emerged across the three dossiers, then generalised to a shelf primitive. Sibling of the thumbnail → modal enlargement plate — the same single-source, control-plane discipline applied to a different page layer. - Fourth beat
- The optional "dreamt" reveal — real Street View → Studio-Ghibli twin on hover, generated with fal.ai FLUX.1 Kontext [max] (~$0.08/frame) via the reusable
fal_ghibli.py. First frames: the Zürich, Dallas and Luxembourg facades. Added 2026-07-13. - Build cost
- ~40 min to lift the three primitives to shared css+js and migrate the three origin pages to import them; a few lines of markup per new dossier thereafter. The dreamt overlay is one ~$0.08 image-to-image call + the exact-size resize per frame.