Plate № 24 · UX · maps / location

A pattern from the gf.cx specimen book

The good-map pattern

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".

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:

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.

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.

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:

Where it applies

Reusable elements

The named, copyable pieces — lift any one without the others:

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 · .rollimg reveal · .svfacade facade · .embed3d box · the exact-size overlay rule (listed above).
Origin
Bespoke .rollimg / .svfacade / .embed3d hand-wired into srvc.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.