Their heading highlight, and the page head tab 1 never had
A 68-of-68 computed-style diff was reporting the region page identical while two visible things were wrong. 1. THEIR TITLES HIGHLIGHT THEIR LAST WORD. Every heading on their regions surface is <h1 class="highlight" data-highlight-last>, and their useTextHighlight wraps the final word in .color-primary. Measured live: /regions/africa is ENTIRELY rgb(146,177,149) because "Africa" is the last word, and /regions/middle-east renders 'Middle <span class="color-primary">East</span>'. Ours was entirely dark. ★ HOW A 68/68 DIFF MISSED AN ENTIRELY GREEN HEADING: the h1 spec listed fontSize, lineHeight, fontFamily, textAlign and textTransform — and not COLOUR. The lesson is not "add colour to the list". It is that a property absent from the spec is NOT MEASURED, and unmeasured is not identical. Same shape as the font miss, where both sides named Poppins and only a glyph-width oracle showed we were not rendering it. Colour now covers the h1, its highlight span, the stat values, the headline figures and the goal lines: 68 -> 73 properties, still 0 differ. I found this reading their source for a different card, not from a check. Ported declaratively rather than as their DOM walk. Theirs mutates innerHTML once onMounted and stamps a processed flag — right for a Nuxt page whose title never changes. Ours changes on every selection, so an imperative one-shot would either fight Vue's re-render or go stale after the first subject. Same logic, expressed where Vue can keep it true. 2. TAB 1 HAD NO PAGE HEAD AT ALL. pages/regions/index.vue:47-58 opens with that h1 plus a .color-brand-lighter strapline; we rendered only the region cards. Invisible inside the composite, where the tab strip names the view, and plainly missing on the regions-list leaf, which is a page in its own right. ★ AND THE IMAGE CAUGHT MY OWN DEFECT. A whitespace-only <span> </span> between two inline elements COLLAPSES: "Middle East" rendered "MIDDLEEAST". Every assertion was green — the colour was right, the span was there, the text content was right once whitespace-normalised. Only the screenshot showed it. The space now lives inside the text interpolation, and an innerHTML assertion guards it that was first shown to FAIL on the collapsed form. Verified: region 73/73 vs live, country 28/28, mobile 44/0 and 26/0 at 390+768, i18n, chooser and dashboard suites green, no gate newly red.
This commit is contained in:
parent
bc06a03ec6
commit
efe2e506a8
8 changed files with 102 additions and 32 deletions
|
|
@ -35,6 +35,7 @@ import UupgsList from './src/UupgsList.vue'
|
|||
// the stat labels and the table headers are all t() calls on their page; hardcoding the
|
||||
// English was the last thing keeping this clone monolingual.
|
||||
import { useRegionsI18n } from './src/useRegionsI18n.js'
|
||||
import { splitLastWord } from './src/useLastWordHighlight.js'
|
||||
// Region colours come from the shared strategy (check:one-palette), and REGION_ORDER is the
|
||||
// order its CVD separation was validated in — the legend must render in it for that to hold.
|
||||
import { PALETTE as REGION_PALETTE, WAGF_REGIONS, REGION_ORDER } from '@map/colors/wagf-region.js'
|
||||
|
|
@ -95,6 +96,8 @@ useDocumentFont('Poppins', POPPINS_FACES, assetsBase.value)
|
|||
|
||||
// ── state ───────────────────────────────────────────────────────────────────
|
||||
const { t, tf , locale } = useRegionsI18n()
|
||||
// their <h1 class="highlight" data-highlight-last> — the final word in brand primary
|
||||
const titleParts = computed(() => splitLastWord(subject.value?.title || ''))
|
||||
const isRTL = computed(() => RTL_LOCALES.has(locale.value))
|
||||
|
||||
const regions = ref([])
|
||||
|
|
@ -685,6 +688,9 @@ useShadowStyles(
|
|||
' font-family:"Bebas Neue","Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
|
||||
' font-weight:400; }' +
|
||||
// their .color-brand-lighter summary under the h1
|
||||
// .color-primary on the highlighted word — --color-brand-primary, measured live as
|
||||
// rgb(146,177,149). The h1 itself keeps --color-text-on-default.
|
||||
'.rd-title__last{ color:var(--dx-accent); }' +
|
||||
'.rd-summary{ margin:0; color:var(--dx-muted); font-size:19.5312px; }' +
|
||||
'.rd-prompt{ margin:0; color:var(--dx-muted); font-size:15.625px; }' +
|
||||
|
||||
|
|
@ -841,7 +847,7 @@ useShadowStyles(
|
|||
<p v-if="!isAll" class="rd-backlink">
|
||||
<button type="button" class="rd-light-link" @click="clearSelection">← {{ t('All Regions') }}</button>
|
||||
</p>
|
||||
<h1 class="rd-title">{{ subject.title }}</h1>
|
||||
<h1 class="rd-title">{{ titleParts.head ? titleParts.head + ' ' : '' }}<span class="rd-title__last">{{ titleParts.last }}</span></h1>
|
||||
<p v-if="subject.kind === 'country'" class="rd-summary">
|
||||
{{ formatNumber(subject.stats.count) }} {{ t('People Groups') }} ·
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
*/
|
||||
import { inject, ref, computed, onMounted, getCurrentInstance } from 'vue'
|
||||
import { useShadowStyles } from '@map/composables/useShadowStyles.js'
|
||||
import { splitLastWord } from './src/useLastWordHighlight.js'
|
||||
import { useRegionsI18n } from './src/useRegionsI18n.js'
|
||||
// ar renders right-to-left. RTL_LOCALES is the shared set and :dir on our own root is the
|
||||
// house pattern (doxa-simple-map.vue:18, :246, :1346) — an EMBEDDED element must not reach for
|
||||
// document.documentElement.dir, which belongs to the host page.
|
||||
|
|
@ -79,6 +81,15 @@ const assetsBase = computed(() => cfg.value.assetsBase || './assets')
|
|||
const { locale: i18nLocale } = useI18n()
|
||||
const isRTL = computed(() => RTL_LOCALES.has(i18nLocale.value))
|
||||
|
||||
// ★ THEIR PAGE HEAD, WHICH THIS PROFILE DID NOT HAVE AT ALL.
|
||||
// pages/regions/index.vue:47-58 opens with a centred .stack--xs of
|
||||
// <h1 class="highlight" data-highlight-last>Regions</h1> and a .color-brand-lighter subtitle.
|
||||
// We rendered only the region cards, so the standalone leaf page had no title and no strapline —
|
||||
// invisible inside the composite, where the tab strip names the view, and plainly missing on
|
||||
// /doxa-maps/regions-dashboard/regions-list/index.html, which is a page in its own right.
|
||||
const { t } = useRegionsI18n()
|
||||
const headTitle = computed(() => splitLastWord(t('Regions')))
|
||||
|
||||
// ★ REGISTER BEBAS NEUE AT DOCUMENT LEVEL, not in the shadow root: @font-face is document-scoped
|
||||
// and a face declared inside a shadow root is ignored. This profile registers it too, and not
|
||||
// only tab 2, because its own leaf page (regions-list/index.html) is a standalone surface where
|
||||
|
|
@ -300,6 +311,15 @@ useShadowStyles(
|
|||
// box-sizing:border-box so .rl-status's own 28px padding stays inside the cap.
|
||||
'.rl-scroll > *{ box-sizing:border-box; width:calc(90% + 16px); max-width:1200px;' +
|
||||
' margin-left:auto; margin-right:auto; }' +
|
||||
// Their head block: .stack--xs (10px) gap, centred. h1 is _globals' clamp(--font-size-3xl,
|
||||
// 7vw, --font-size-5xl) and the subtitle is --font-size-md in --color-brand-lighter.
|
||||
// cqw not vw, for the same composite-pane reason as every other clamp here.
|
||||
'.rl-head{ display:flex; flex-direction:column; gap:10px; text-align:center; }' +
|
||||
'.rl-title{ margin:0; font-size:clamp(47.6837px, 7cqw, 74.5058px); line-height:1;' +
|
||||
' font-family:"Bebas Neue","Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
|
||||
' font-weight:400; text-transform:uppercase; }' +
|
||||
'.rl-title__last{ color:#92b195; }' +
|
||||
'.rl-summary{ margin:0; color:var(--dx-muted); font-size:19.5312px; }' +
|
||||
'.rl-status{ padding:28px; text-align:center; color:var(--dx-muted); font-size:19.5312px; }' +
|
||||
'.rl-status.rl-failed{ font-weight:600; }' +
|
||||
// .region-card: DX-life's --border-radius-xlg (35px), overflow hidden, white body, no border --
|
||||
|
|
@ -381,6 +401,12 @@ useShadowStyles(
|
|||
<template>
|
||||
<div class="rl-root" :dir="isRTL ? 'rtl' : 'ltr'">
|
||||
<div class="rl-scroll">
|
||||
<!-- pages/regions/index.vue:47-58 — their centred head: the title with its last word
|
||||
in brand primary, and the strapline underneath. -->
|
||||
<div class="rl-head">
|
||||
<h1 class="rl-title">{{ headTitle.head ? headTitle.head + ' ' : '' }}<span class="rl-title__last">{{ headTitle.last }}</span></h1>
|
||||
<p class="rl-summary">{{ t('Find unreached people groups by region and country.') }}</p>
|
||||
</div>
|
||||
<div v-if="loading" class="rl-status">Loading regions…</div>
|
||||
<div v-else-if="loadFailed" class="rl-status rl-failed" role="status">
|
||||
Region data could not be loaded. This is a connection problem, not a filter.
|
||||
|
|
|
|||
38
plugin-designs/regions-dashboard/src/useLastWordHighlight.js
Normal file
38
plugin-designs/regions-dashboard/src/useLastWordHighlight.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* useLastWordHighlight.js — their `.highlight` + `data-highlight-last` heading treatment.
|
||||
*
|
||||
* WHAT IT IS. Every page heading on DX-life's regions surface is
|
||||
* `<h1 class="highlight" data-highlight-last>`, and app/composables/useTextHighlight.ts wraps the
|
||||
* LAST WORD of that heading in `<span class="color-primary">`. So their titles read with the final
|
||||
* word in brand primary:
|
||||
* /regions/africa -> the whole title green, because "Africa" IS the last word
|
||||
* /regions/middle-east -> "Middle" in ink, "East" in green
|
||||
* MEASURED on the live pages: h1 colour rgb(42,51,42) with an inner span at rgb(146,177,149).
|
||||
*
|
||||
* ★ HOW IT WAS MISSED. A 68-of-68 computed-style diff of the region page reported no differences
|
||||
* while our titles were entirely dark, because the diff compared the h1's fontSize, lineHeight,
|
||||
* fontFamily and textAlign — and never its COLOUR, nor the existence of an inner span. The lesson
|
||||
* is not "add colour to the list"; it is that a property absent from the spec list is not
|
||||
* measured, and unmeasured is not identical.
|
||||
*
|
||||
* WHY DECLARATIVE AND NOT THEIR DOM WALK. Theirs mutates innerHTML once onMounted and stamps
|
||||
* data-highlightProcessed so re-runs are cheap — correct for a Nuxt page whose title never
|
||||
* changes. Our heading changes on every selection (chooser -> region -> country), so an
|
||||
* imperative one-shot would either fight Vue's re-render or go stale after the first subject.
|
||||
* Same logic, expressed where Vue can keep it true: split the words, wrap the last.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Split a heading for last-word highlighting.
|
||||
* @param {string} text
|
||||
* @returns {{ head: string, last: string }} head = everything before the final word (may be ''),
|
||||
* last = the final word. Their split is on any whitespace run, after trimming, so word
|
||||
* boundaries are right regardless of how the template was formatted.
|
||||
*/
|
||||
export function splitLastWord(text) {
|
||||
const words = String(text ?? '').trim().split(/\s+/).filter(Boolean)
|
||||
if (!words.length) return { head: '', last: '' }
|
||||
const last = words[words.length - 1]
|
||||
const head = words.slice(0, -1).join(' ')
|
||||
return { head, last }
|
||||
}
|
||||
8
public/doxa-maps/manifest.json
generated
8
public/doxa-maps/manifest.json
generated
|
|
@ -838,7 +838,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz",
|
||||
"integrity": "sha384-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -858,7 +858,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-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz\" 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-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -872,7 +872,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-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz\" 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-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+\" 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>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -886,7 +886,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-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz\" 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-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+\" 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>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -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-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -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-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-W0oDAJmpV9sW5+jcEQSY6rPPcbw3+bjGjMNqDlBo8Py5eYgZ2bSKaijh88eA15a+" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue