3D & massing
How the map draws volume: existing buildings, the planning envelope and a generated proposal, each at its own opacity. Numbers measured from the shipping map.
Buildings fade in
Context buildings are fill-extrusions whose opacity is interpolated on zoom: 0 at 14.5, 0.75 at 15.5.
Code
import { fabric } from "@gridd/ui/cartography"
map.addLayer({
id: "buildings-ext",
type: "fill-extrusion",
source: "buildings",
"source-layer": "bldg",
minzoom: 14.5,
paint: {
"fill-extrusion-color": fabric().building,
// interpolated on ZOOM, not a transition: it tracks the camera in both
// directions and settles on the 0.75 the layer has always rendered at
"fill-extrusion-opacity": ["interpolate", ["linear"], ["zoom"], 14.5, 0, 15.5, 0.75],
"fill-extrusion-height": ["coalesce", ["get", "h"], 3],
"fill-extrusion-vertical-gradient": true
}
})The solidity ladder
Opacity is rank. The thing being proposed is the most solid thing on the map.
0.90.750.42Code
import { massing } from "@gridd/ui/cartography"
import { token } from "@gridd/ui/tokens"
// the planning envelope — the selection accent, at 0.42
map.addLayer({
id: "envelope-ext",
type: "fill-extrusion",
source: "envelope",
paint: {
"fill-extrusion-color": token("--gd-accent", theme),
"fill-extrusion-opacity": 0.42,
"fill-extrusion-base": ["get", "base"],
"fill-extrusion-height": ["get", "top"],
"fill-extrusion-vertical-gradient": true
}
})
// the proposal — teal, alternating by storey parity, at 0.9. Inserted BELOW
// the envelope so it writes depth first and the ghosted envelope blends over it.
map.addLayer({
id: "massing-ext",
type: "fill-extrusion",
source: "massing",
paint: {
"fill-extrusion-color": [
"case", ["==", ["%", ["coalesce", ["get", "storey"], 1], 2], 0],
massing().b, massing().a
],
"fill-extrusion-opacity": 0.9,
"fill-extrusion-vertical-gradient": true
}
}, "envelope-foot")
// a proposal lands: ghost the envelope, hide the existing stock
map.setPaintProperty("envelope-ext", "fill-extrusion-opacity", proposal ? 0.14 : 0.42)0.9— the PROPOSAL: GRIDD's generated best-fit massing, in the cartography teal (#17A6A0, alternating with#0F7D78by storey parity).0.75— context buildings at full zoom, in the neutral stock colour.0.42— the 3D planning envelope, in the selection accent. It ghosts to0.14the moment a proposal is placed inside it, and the existing building stock hides outright.
Rules
- Fade extrusions in over a full zoom level — 0 at 14.5, 0.75 at 15.5. Interpolate on zoom, not on a transition.
- Default a missing height to one storey —
coalesce(h, 3). - Turn
fill-extrusion-vertical-gradienton for EVERY extrusion, including the proposal. - Draw context buildings in the neutral stock colour (
#E8E9ED). Buildings are furniture, never a signal. - Draw the proposal in the cartography teal, never the ramp's pass-green.
- Ghost the envelope to 0.14 and hide the building stock while a proposal is on the lot.
- Draw easements BEFORE the envelope, so the carved-out box reads as clear of them.
- Land every ordinary camera move flat and north-up. Leaving 3D is one ease, 600–700 ms.
- Do not force pitch 0 while an envelope is up. The reframe owns tilt exactly then.
Why this way
The reasoning behind the decisions above. Read it once; it is what stops the next person undoing them.
Why buildings fade over a whole level
A city of prisms popping into one frame is the worst frame the map can paint. A full level of fade means no frame is the arrival frame — the Google-Maps building entrance. It is zoom-interpolated rather than a CSS-style transition so it tracks the camera continuously in both directions, and settles on the same 0.75 the layer has always rendered at.
Opacity is rank
The thing being proposed is the most solid thing on the map and everything else yields to it. Context at 0.75 is solid enough to read and transparent enough to see a boundary through; the envelope at 0.42 is a rule volume you are meant to look INTO. When a proposal arrives the envelope drops to 0.14 and the existing stock hides outright, because at that moment the question has narrowed to one building and everything else is in the way of it.
Why the proposal is teal and not green
Green borrows the assessment ramp's verdict vocabulary, and a generated massing is a study model, not a verdict. The teal also clears the warm existing stock and the indigo envelope it sits inside, so a proposal reads at a glance. The two teal shades alternate by storey parity so a stack of levels is legible as distinct boxes rather than one fused block.
The massing layer is inserted BELOW the envelope layers on purpose: extrusion layers depth-test against earlier-drawn geometry, so the massing has to write depth first and let the ghosted envelope blend over it — otherwise the envelope wall occludes the very thing it is meant to contain.
The building colour, and why it is not pure white
#E8E9ED: a near-white, deliberately not pure white. The layer is semi-transparent so a parcel's fill still reads through it, and pure white picks up a cold tint from whatever is beneath it. Buildings and boundaries are context; the palette keeps them quiet on purpose.
One storey is an honest default, not a guess
Height is coalesce(h, 3) — where the storeys model has no answer, a building is one storey. In a detached-housing fabric that is the honest answer rather than a guess dressed as data, and it fails in the direction that under-claims.
One sky
fill-extrusion-vertical-gradient is on for every extrusion — the light comes from one place, including on the proposal. An unlit proposal inside lit context reads as a cut-out rather than as a building.
2D is the working view
3D is a mode the user enters, not a state the map drifts into. Pitch 0 and bearing 0 ride along on every fly, fit and reset; leaving 3D is one ease, 600–700 ms, the same duration class as every other mode reset (Camera & movement). The single exception is that while an envelope is up the reframe owns tilt — forcing pitch 0 before re-tilting paints a one-frame bounce.
The envelope itself is drawn as foot plus walls from the parcel's own base/top attributes: the rule volume is geometry, not decoration. Easements are drawn before it so the carved-out box reads as clear of them.