Draw the pin as one circle with a white stroke, not three colour bands
Some checks are pending
build-gate / build (push) Waiting to run
Some checks are pending
build-gate / build (push) Waiting to run
The pins read as eyes. The cause is structural: two stacked circle layers produced, outward from the centre at z1, navy disc r=1.6, white field r=3.4, navy stroke 0.6px. Dark centre, light middle, dark rim -- pupil, sclera, iris outline. A comment in the file already called it "a navy/white/navy sandwich" without noticing what a sandwich of those bands draws. Concentric rings sharing a centre are the pattern the visual system fuses into one object, which is why a bullseye is named after an eye. The inner disc sat at 47% of the halo radius at z1, the proportion that reads as a pupil rather than a dot with a border, and worst when zoomed out -- where a world map lives. The outer navy band is the specific culprit: it closes the iris. Removing it leaves the same colours reading as an ordinary pin, so this deletes a band rather than changing a colour. Both Driver rulings survive: blue dots, white rings, one fixed colour at full opacity. One layer with a stroke is also the documented Mapbox idiom -- circle-stroke-width draws outside circle-radius, so a single layer already gives disc-plus-ring, and the old code hand-rolled that and collected a third band for the trouble. Stroke now scales with zoom, the documented way to stop a ring swallowing the fill at low zoom. Cartographic practice agrees: Axis Maps and the ICA Commission both put casings on symbols that OVERLAP rather than a global opaque halo, and Esri names halos the biggest cost in marker drawing performance -- paid here on a second full circle layer per point. Research and sources in _qa-cycle/01-ideation-research/RESEARCH-pin-design-why-they-read-as-eyes.md Not yet verified: no hardware WebGL here, so the appearance is unproven. The pale end of the ramp needs measuring, since the outer band was what gave the white ring a hard boundary on any ground, and a perception complaint needs the Driver's eye rather than a contrast ratio. Also removes the table header caption "Filtering or selecting above updates this list". Its centring history is kept in a comment: it records a reversed judgement, not a mistake -- v1 put it in a second header, it was moved to the existing bar on request, and centred by absolute positioning because flex spacers landed it 55px off centre. All correct, and it still had to go: it narrates the one behaviour on this surface that needs no narration.
This commit is contained in:
parent
fd41b4a207
commit
2f5cf655f4
2 changed files with 73 additions and 21 deletions
|
|
@ -491,19 +491,25 @@ useShadowStyles(`
|
|||
</template>
|
||||
<span class="rd-bar-sp"></span>
|
||||
<!--
|
||||
★ THE HINT LIVES IN THE EXISTING HEADER — NOT A SECOND ONE (Driver, v1 rejected).
|
||||
v1 added this as its own <p> above the grid. The Driver's correction: "you dont have to add
|
||||
a SECOND HEADER to make this show up... it could be CENTRED on the same header where it says
|
||||
total 2107. Why? because we do not [want the extra chrome]". So it rides the bar that is
|
||||
already there, beside the live count, and costs ZERO extra height.
|
||||
★ NO EXPLANATORY HINT HERE (Driver, 2026-09-22: "there does not need to be this text in the
|
||||
table header / Filtering or selecting above updates this list").
|
||||
|
||||
CENTRED BY ABSOLUTE POSITIONING against the bar, not by flex spacers. A first attempt put
|
||||
it between two spacers and MEASURED 55px OFF CENTRE at 1440px and 1280px: spacers split the
|
||||
LEFTOVER space, so the hint lands mid-gap, not mid-bar, because the left cluster
|
||||
(dock/title/count/selection) and the right cluster (toast/Export CSV) differ in width.
|
||||
Out of flow also means it adds zero height by construction.
|
||||
The text read "Filtering or selecting above updates this list". Its history is worth keeping
|
||||
because it records a judgement that got REVERSED rather than a mistake: v1 put it in a second
|
||||
header above the grid, the Driver asked for it to ride the EXISTING bar instead, and it was
|
||||
centred by absolute positioning against the bar (flex spacers split the LEFTOVER space, so
|
||||
the hint landed 55px off centre at 1440px and 1280px because the left and right clusters
|
||||
differ in width). All of that was right, and the text still had to go.
|
||||
|
||||
THE REASON IT GOES: the behaviour it described is the one thing on this surface that needs no
|
||||
description. Filter above, the list below changes — the user sees the count move the moment
|
||||
they act. A caption narrating visible feedback is chrome that costs attention on every view
|
||||
to explain something learned once, and never again.
|
||||
|
||||
Keep .rd-bar-hint's CSS: it is the centring recipe, and re-deriving it would repeat the
|
||||
55px-off-centre measurement. If a future caption genuinely earns its place, reuse the class.
|
||||
-->
|
||||
<span class="rd-bar-hint" data-grid-hint>Filtering or selecting above updates this list</span>
|
||||
|
||||
<span v-if="exportToast" class="rd-toast" data-export-toast>{{ exportToast }}</span>
|
||||
<span class="rd-csv" :class="{ busy: exporting }" role="button" tabindex="0" data-csv-btn
|
||||
:aria-busy="exporting ? 'true' : 'false'"
|
||||
|
|
|
|||
|
|
@ -320,11 +320,12 @@ function addLayers() {
|
|||
// '#fff' at full opacity, because the Driver asked for WHITE rings and 0.95 alpha over a
|
||||
// grey choropleth bin was rendering them off-white.
|
||||
//
|
||||
// HIT-TESTING is unchanged: 'rd-bubble-layer' keeps its id and stays the TOP layer —
|
||||
// onBubbleClick, onMapClick's pin-guard and the __rdashPinAt verify hook all query it by
|
||||
// name. 'rd-bubble-halo' remains a separate, NON-interactive layer beneath it. Note the
|
||||
// hit target shrinks with the dot at low zoom; __rdashPinAt already queries a +/-7px bbox,
|
||||
// which still covers the z1 footprint.
|
||||
// HIT-TESTING is unchanged: 'rd-bubble-layer' keeps its id — onBubbleClick, onMapClick's
|
||||
// pin-guard and the __rdashPinAt verify hook all query it by name. As of 2026-09-22 it is the
|
||||
// ONLY bubble layer: 'rd-bubble-halo' was deleted when the pin collapsed to one circle with a
|
||||
// white stroke (see the long note below). Note the hit target shrinks with the dot at low zoom;
|
||||
// __rdashPinAt already queries a +/-7px bbox, which still covers the z1 footprint — and the
|
||||
// footprint GREW slightly (r 1.6 -> 2.2 at z1), so the existing bbox remains sufficient.
|
||||
// ── ★ THE PIN IS NOW SELF-CONTAINED (card BUILD-MAP-STYLE-MUST-BE-BEAUTIFUL) ──────────
|
||||
// The white ring used to depend on the GROUND for its visibility, and measured only
|
||||
// 1.09–1.24 against the light bins — so on pale countries the ring vanished and the dots
|
||||
|
|
@ -346,12 +347,57 @@ function addLayers() {
|
|||
//
|
||||
// HIT-TESTING unchanged: 'rd-bubble-layer' keeps its id and stays the TOP layer; the halo
|
||||
// is a separate NON-interactive layer beneath it and must not be queried.
|
||||
m.addLayer({ id: 'rd-bubble-halo', type: 'circle', source: 'rd-bubbles',
|
||||
paint: { 'circle-radius': ['interpolate', ['linear'], ['zoom'], 1, 3.4, 6, 7.4], 'circle-color': '#fff', 'circle-opacity': 1,
|
||||
// The outer edge: thin, navy, so the white ring has a hard boundary on ANY ground.
|
||||
'circle-stroke-width': ['interpolate', ['linear'], ['zoom'], 1, 0.6, 6, 1], 'circle-stroke-color': '#1a237e', 'circle-stroke-opacity': 0.9 } })
|
||||
// ── ★ ONE CIRCLE WITH A WHITE STROKE — NOT A NAVY/WHITE/NAVY SANDWICH ──────────────────────
|
||||
// (Driver 2026-09-22: "the pins look like eyes, they look kind of creepy")
|
||||
//
|
||||
// THE SANDWICH WAS THE BUG, and the comment above described it accurately without noticing what
|
||||
// it draws. Read outward from the centre at z1 the old construction was:
|
||||
// navy disc r=1.6 → white field r=3.4 → navy stroke 0.6px
|
||||
// dark centre, light middle, dark rim. That is the anatomy of an eye — pupil, sclera, iris
|
||||
// outline — and it is why a bullseye is NAMED after one. Concentric rings sharing a centre are
|
||||
// exactly the pattern the visual system fuses into a single object; sighting equipment exploits
|
||||
// the same involuntary alignment. The inner disc sat at 47% of the halo radius at z1, squarely in
|
||||
// the proportion that reads as a PUPIL rather than as a dot with a border. Worst when zoomed out,
|
||||
// which is where a world map lives.
|
||||
//
|
||||
// THE OUTER NAVY BAND IS THE SPECIFIC CULPRIT: it closes the iris. Without it the same colours
|
||||
// read as an ordinary pin. So the fix removes a band rather than changing a colour.
|
||||
//
|
||||
// MATCHES THE MAPBOX IDIOM, which is ONE layer with a stroke, not two stacked circles. The style
|
||||
// spec draws `circle-stroke-width` OUTSIDE `circle-radius`, so a single layer already produces
|
||||
// disc-plus-ring; the old code hand-rolled that and collected a third colour band for the
|
||||
// trouble. Scaling the stroke with zoom (1.2 → 2) is the documented way to stop the ring
|
||||
// swallowing the fill when circles are small and dense at low zoom. Cartographic practice agrees:
|
||||
// Axis Maps and the ICA Commission both call for casings on symbols that OVERLAP rather than a
|
||||
// global opaque halo, and Esri names halos the single biggest cost in marker drawing performance
|
||||
// — we were paying that on a second full circle layer across every point. Research + sources:
|
||||
// _qa-cycle/01-ideation-research/RESEARCH-pin-design-why-they-read-as-eyes.md
|
||||
//
|
||||
// ★ THE TWO DRIVER RULINGS THIS PRESERVES — do not undo either:
|
||||
// 1. ONE FIXED COLOUR AT FULL OPACITY. Pins were once '#e67e22' at circle-opacity 0.6; the
|
||||
// alpha was the real fault, compositing each pin with whatever bin sat beneath it so it
|
||||
// rendered a different colour per country at 1.18–1.22 contrast. Never reintroduce pin
|
||||
// alpha, never tint per bin.
|
||||
// 2. WHITE RINGS around blue dots so the field does not read as a heat map, and all rings stay
|
||||
// visible when zoomed out. Still here — it is now the stroke instead of a second layer.
|
||||
// Navy #1a237e measures 12.24 / 11.09 / 10.20 / 9.26 / 8.20 / 7.29 across the ramp; unchanged.
|
||||
//
|
||||
// WHAT IS GIVEN UP, stated honestly: the outer navy band gave the white ring a hard boundary on
|
||||
// ANY ground, so over the palest bin the ring now blends into the background. The ring's job is
|
||||
// separating pins from EACH OTHER (it still does that) while the navy disc carries pin-vs-ground
|
||||
// contrast. MEASURE the pale end before calling this settled. If separation genuinely fails
|
||||
// there, the fix is a casing on overlapping symbols only — not a restored global dark rim.
|
||||
//
|
||||
// HIT-TESTING: 'rd-bubble-layer' keeps its id and is now the ONLY bubble layer, so every
|
||||
// click/hover handler bound to that id is unaffected. 'rd-bubble-halo' no longer exists.
|
||||
m.addLayer({ id: 'rd-bubble-layer', type: 'circle', source: 'rd-bubbles',
|
||||
paint: { 'circle-radius': ['interpolate', ['linear'], ['zoom'], 1, 1.6, 6, 5], 'circle-color': '#1a237e', 'circle-opacity': 1 } })
|
||||
paint: {
|
||||
'circle-radius': ['interpolate', ['linear'], ['zoom'], 1, 2.2, 6, 5.5],
|
||||
'circle-color': '#1a237e', 'circle-opacity': 1,
|
||||
'circle-stroke-color': '#ffffff',
|
||||
'circle-stroke-width': ['interpolate', ['linear'], ['zoom'], 1, 1.2, 6, 2],
|
||||
'circle-stroke-opacity': 1,
|
||||
} })
|
||||
// selection pen — a prominent neon ring over the selected PG pin(s), drawn ABOVE the normal bubbles.
|
||||
m.addSource('rd-bubble-selected', { type: 'geojson', data: selectedGeo() })
|
||||
m.addLayer({ id: 'rd-bubble-selected', type: 'circle', source: 'rd-bubble-selected',
|
||||
|
|
|
|||
Loading…
Reference in a new issue