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.
38 lines
2.1 KiB
JavaScript
38 lines
2.1 KiB
JavaScript
/**
|
|
* 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 }
|
|
}
|