Copy their list bullet instead of my own, and stop shipping rounded tokens

Two findings on the regions LIST tab, both from extending a diff that had been
reporting a clean pass.

THE BULLET WAS MINE, NOT THEIRS. RULE 1 says copy, don't recreate, and I had
recreated their list marker as a 6px border-radius:50% dot. base/_globals.scss
:117-131 paints every list marker on their site with a 16x16 data-URI SVG
circle in #92b195 sized to 1.5em, hung off the text's starting edge by
margin-inline-start:-1.3em (-2em in RTL), top .3em, line-height .9 — and their
.countries-grid carries a comment saying keeping it is deliberate. Against live
mine was wrong in 9 of 11 properties: 6px against 29.2969px, a solid fill
against a transparent box carrying the image, top 11.72px against 5.86px,
positioned by inset-inline-start:-16px instead of their negative margin. Two
matched. Not a near-miss — a different bullet. Theirs is now copied, and
.countries-grid's physical padding-left:35.2px is replaced by their logical
padding-inline-start:1.8em, because the physical one put the indent on the
wrong side in Arabic, where the bullets hang off the right edge.

THE TOKENS WERE ROUNDED, AND EM MULTIPLIES THE ERROR. With their bullet ported
it still measured 29.2812px against their 29.2969px. 0.0157px is nothing to
look at, but it is the rounding of --font-size-md (19.5312 for their 19.53125)
times 1.5, and every em- and percentage-derived length on the page inherits the
same error. dxlife-tokens.js said re-deriving to live's precision would be "a
separate, separately measured step"; this is that step. Both scales are exact
powers of their two ratios on a 10px root — type 1.25, spacing 1.33 — so full
precision costs nothing. The bullet now matches on all 11 properties exactly,
and the region card on all 32.

The diff could not have found either before. getComputedStyle(el) does not see
::before and no spec named one, so a pseudo-element that was wrong in nine ways
sat next to "32 of 32 identical". The probe now reads the bullet as a pair on
both sides — and compares its backgroundImage after normalisation, because
their live CSS serialises the SVG with xmlns first while the checked-out SCSS
writes it last: their deployed stylesheet is not byte-for-byte the source, so a
raw compare would report a permanent difference for an identical bullet while
blanket-accepting it would hide a real change of image. The comparison
percent-decodes, drops the charset Chrome adds and sorts the attribute tokens —
sensitive to the circle, its radius and its colour, blind only to their order.

Also here: the maps render the globe projection, measured rather than argued —
projection=globe on both the chooser and a region page, via a constructor trap
installed before any page script. And the probe's own two defects fixed: it
asserted "our style JSON does not override the projection" and failed, which
was my guess about where the value comes from rather than a requirement
(Mapbox Standard declares globe itself, which is why the default is globe); and
it read the map list from the main frame only. The remaining honest gap is
named in the file and on the card: their CountryMap imports mapbox-gl as a
bundled ESM module, so no global exists to trap and the ours-vs-theirs half is
unproven until a pixel oracle with a forced-mercator negative control exists.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 19:55:03 +00:00
parent f22609fb3c
commit d5effe7c66
8 changed files with 62 additions and 37 deletions

View file

@ -20,10 +20,19 @@
// rem values would resolve against the host's root (16px by default) and every space would be
// 1.6x too large. That is exactly why these were resolved to px in the first place.
//
// THE VALUES ARE OURS AS THEY ALREADY WERE, not re-derived from live. Live carries more precision
// (2.352637rem = 23.52637px vs our 23.5264px). Naming the numbers and changing the numbers are two
// different changes; this one only names them, which is what lets a full computed-style
// fingerprint prove it altered nothing. Re-deriving would be a separate, separately measured step.
// THE VALUES ARE NOW LIVE'S FULL PRECISION. They started as the rounded numbers this file was
// created to name (23.5264px for --spacing-lg, where live is 2.352637rem = 23.52637px), and the
// header said re-deriving them would be "a separate, separately measured step". THIS IS THAT STEP,
// and a measurement forced it: their country-list bullet is sized in em, 1.5em of --font-size-md,
// and against live it came out 29.2812px where theirs is 29.2969px. 0.0157px, which is nothing to
// look at — but it is the ROUNDING of --font-size-md (19.5312 for their 19.53125) multiplied by
// 1.5, and every other em- or percentage-derived length on the page inherits the same error. The
// scales are exact powers of their two ratios (type 1.25, spacing 1.33) on a 10px root, so full
// precision is available for free and there is no reason to ship a rounded clone.
// type: 10px * 1.25^n -> 15.625 / 19.53125 / 24.4140625 / 30.517578125 / ...
// spacing: 10px * 1.33^n -> 13.3 / 17.689 / 23.52637 / 31.2900721 / ...
// The vs-live diffs compare with a +/-0.5px tolerance, so this cannot be what turns one green; the
// bullet's exact-equality compare is what it is for.
//
// Mapping verified against live's own custom properties, not arithmetic:
// spacing 3xs .5653230821rem · 2xs .7518796992 · md 1.7689 · lg 2.352637 · xl 3.12900721
@ -36,10 +45,10 @@
// a magic number because it reads as deliberate.
export const DXLIFE_TOKENS =
':host{' +
' --spacing-3xs:5.6532px; --spacing-2xs:7.5188px; --spacing-md:17.689px;' +
' --spacing-lg:23.5264px; --spacing-xl:31.2901px; --spacing-2xl:41.6158px;' +
' --spacing-3xl:55.349px; --spacing-4xl:73.6142px;' +
' --font-size-sm:15.625px; --font-size-md:19.5312px; --font-size-lg:24.4141px;' +
' --font-size-xl:30.5176px; --font-size-2xl:38.147px; --font-size-3xl:47.6837px;' +
' --font-size-4xl:59.6046px; --font-size-5xl:74.5058px;' +
' --spacing-3xs:5.653230821px; --spacing-2xs:7.518796992px; --spacing-md:17.689px;' +
' --spacing-lg:23.52637px; --spacing-xl:31.2900721px; --spacing-2xl:41.615795893px;' +
' --spacing-3xl:55.349008538px; --spacing-4xl:73.614181355px;' +
' --font-size-sm:15.625px; --font-size-md:19.53125px; --font-size-lg:24.4140625px;' +
' --font-size-xl:30.517578125px; --font-size-2xl:38.146972656px; --font-size-3xl:47.68371582px;' +
' --font-size-4xl:59.604644775px; --font-size-5xl:74.505805969px;' +
'}'

View file

@ -425,7 +425,9 @@ useShadowStyles(
// one list-item's worth even though DX-life's countries-grid a rule doesn't set it directly.
// Reproduced with list-style:none + the same 35.16px indent kept explicitly so a real list
// bullet has somewhere to sit without stealing width from the text column.
'.countries-grid{ list-style:none; margin:0; padding:0 0 0 35.2px; display:grid;' +
// padding-INLINE-start, as theirs is (1.8em). The physical `padding-left:35.2px` it replaces
// put the indent on the wrong side in Arabic, where the bullets hang off the right edge.
'.countries-grid{ list-style:none; margin:0; padding-inline-start:1.8em; display:grid;' +
' grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:10px var(--spacing-xl); }' +
// Plain inline text (not a flex row) so multi-line country names wrap the same way DX-life's
// <NuxtLink> text does -- a flex row with align-items:baseline pinned the bullet to the first
@ -441,8 +443,22 @@ useShadowStyles(
// inset-inline-start, NOT left: the bullet hangs off the text's starting edge, which is the
// RIGHT edge in Arabic. With `left:-16px` it stayed on the physical left in RTL — measured
// at locale=ar, computed right was 256.938px instead of the -16px near-edge overhang.
'.country-link::before{ content:""; position:absolute; inset-inline-start:-16px; top:0.6em; width:6px; height:6px;' +
' border-radius:50%; background:#92b195; }' +
// ★ THEIR BULLET, COPIED — not a dot of my own. base/_globals.scss:117-131 paints the list
// marker with a 16x16 data-URI SVG circle in #92b195 sized to 1.5em, hung off the text's start
// edge by margin-inline-start:-1.3em (-2em in RTL), top .3em, line-height .9. Their
// .countries-grid comment is explicit that keeping it is deliberate ("Keeps the site's list
// bullets (global ul padding + ::before dot)").
// I had re-created it as a 6px border-radius:50% square, and MEASURED against live it was wrong
// in 9 of 11 properties: 6px vs 29.2969px box, solid fill vs a transparent box carrying the
// image, top 11.72px vs 5.86px, and positioned with inset-inline-start:-16px instead of their
// negative margin. Not a near-miss — a different bullet.
// The rule is a double-quoted JS string because the data URI contains single quotes.
".country-link::before{ content:''; position:absolute; top:0.3em; width:1.5em; height:1.5em;" +
" line-height:0.9; margin-inline-start:-1.3em; background-repeat:no-repeat;" +
" background-image:url(\"data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='%2392b195' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='8' cy='8' r='7' stroke='%2392b195' stroke-width='1.5'/%3E%3C/svg%3E\"); }" +
// Their RTL variant. It is a separate rule here rather than a nested [dir="rtl"] & because this
// CSS lives in a shadow root, where a [dir] selector on an outer ancestor does not reach in.
'[dir="rtl"] .country-link::before{ margin-inline-start:-2em; }' +
'.country-link:hover{ text-decoration:underline; text-decoration-color:#92b195; }' +
'.country-link:focus-visible{ outline:2px solid #92b195; outline-offset:2px; border-radius:4px; }' +
'.country-link__count{ color:var(--dx-muted); }' +

File diff suppressed because one or more lines are too long

View file

@ -879,7 +879,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl",
"integrity": "sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI",
"elements": [
"regions-dashboard"
],
@ -899,7 +899,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -913,7 +913,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -927,7 +927,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-MeHrLZLEBmvYM0NTxaJJTnLJ5UIr17K0HuHntIvLbzIXTqtr6/aJYTs16wezXpgl" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-tOMQpTMEi9guhjJ1zxSXhSSENJJVj3HpNS8JfvA46dbJxU62kaA19WvdfYmYYzNI" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: