diff --git a/plugin-designs/regions-dashboard/region-dashboards.vue b/plugin-designs/regions-dashboard/region-dashboards.vue index be7699a1f..575ac2e3e 100644 --- a/plugin-designs/regions-dashboard/region-dashboards.vue +++ b/plugin-designs/regions-dashboard/region-dashboards.vue @@ -46,6 +46,7 @@ import { PALETTE as REGION_PALETTE, WAGF_REGIONS, REGION_ORDER } from '@map/colo // component's shadow CSS so every var() below resolves inside the shadow root. import { DXLIFE_TOKENS } from './dxlife-tokens.js' import RegionCountryMap from './src/RegionCountryMap.vue' +import { SHADOW_BOX_SIZING_RESET } from './src/shadowReset.js' import { summarizeRegions, emptyStats, toNumber, PEOPLE_GROUP_LIST_FIELDS } from '@map/utils/countries-meta.js' const props = defineProps({ @@ -704,6 +705,9 @@ onBeforeUnmount(() => { // the two tabs does not change the surface under the reader. // NO BACKTICKS in this string, including comments (house style: one terminates the literal). useShadowStyles( + // Their _reset.scss:3-8 FIRST, so every rule below is authored against border-box exactly as + // theirs is. See src/shadowReset.js for why it lives there and for the measured blast radius. + SHADOW_BOX_SIZING_RESET + DXLIFE_TOKENS + // EVERY value below is DX-life's own, resolved against their 10px rem root // (app/assets/styles/base/_globals.scss html{font-size:var(--font-size-base)}, and @@ -793,7 +797,7 @@ useShadowStyles( ' padding:var(--spacing-3xl) 0 var(--spacing-4xl); display:flex; flex-direction:column;' + ' container-type:inline-size; }' + '.rd-scroll > * + *{ margin-top:var(--spacing-3xl); }' + // .stack .stack--3xl - '.rd-scroll > *{ box-sizing:border-box; width:calc(90% + 16px); max-width:1200px;' + + '.rd-scroll > *{ width:calc(90% + 16px); max-width:1200px;' + ' margin-left:auto; margin-right:auto; }' + '.rd-status{ padding:28px; text-align:center; color:var(--dx-muted); font-size:var(--font-size-md); }' + '.rd-status.rd-failed{ font-weight:600; }' + @@ -838,7 +842,7 @@ useShadowStyles( // ── .card: blocks/_cards.scss:9-21. radius --border-radius-xlg (35px); // padding clamp(--spacing-lg, 6vw, --spacing-2xl) clamp(--spacing-lg, 6vw, --spacing-3xl). - '.rd-card{ position:relative; box-sizing:border-box; border-radius:var(--dx-radius);' + + '.rd-card{ position:relative; border-radius:var(--dx-radius);' + ' background:var(--dx-elevated); color:var(--dx-text);' + ' padding:clamp(var(--spacing-lg), 6cqw, var(--spacing-2xl)) clamp(var(--spacing-lg), 6cqw, var(--spacing-3xl)); }' + // .card[padding-small] -> --spacing-lg flat diff --git a/plugin-designs/regions-dashboard/regions-list.vue b/plugin-designs/regions-dashboard/regions-list.vue index 552eb49d8..7131ed944 100644 --- a/plugin-designs/regions-dashboard/regions-list.vue +++ b/plugin-designs/regions-dashboard/regions-list.vue @@ -40,6 +40,7 @@ */ import { inject, ref, computed, onMounted, watch, getCurrentInstance } from 'vue' import { useShadowStyles } from '@map/composables/useShadowStyles.js' +import { SHADOW_BOX_SIZING_RESET } from './src/shadowReset.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 @@ -340,6 +341,8 @@ onMounted(loadRegions) // Colours/fonts/radii are an intentional exception to this repo's material-light chrome tokens — // the Driver asked for an exact clone of DX-life's OWN design here, so the clone wins. useShadowStyles( + // Their _reset.scss:3-8 FIRST -- see src/shadowReset.js. + SHADOW_BOX_SIZING_RESET + DXLIFE_TOKENS + ':host{ display:block; width:100%; height:100%; min-height:320px; }' + '.rl-root{ position:absolute; inset:0; display:flex; flex-direction:column; min-height:0;' + @@ -383,7 +386,7 @@ useShadowStyles( // So it is FLUID at 90%+16px until the viewport reaches 1315.625px, then pinned at 1200px centred. // Our cards previously stretched to the full pane at every width -- there was no cap at all. // 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;' + + '.rl-scroll > *{ 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. diff --git a/plugin-designs/regions-dashboard/regions-mfe.vue b/plugin-designs/regions-dashboard/regions-mfe.vue index bf21b46cd..d302e420f 100644 --- a/plugin-designs/regions-dashboard/regions-mfe.vue +++ b/plugin-designs/regions-dashboard/regions-mfe.vue @@ -27,6 +27,7 @@ import { ref, computed, inject, onMounted, onBeforeUnmount } from 'vue' import { buildPaneColumn, PANE_GRID_STYLES } from '@map/layouts/pane-wrapper/pane-wrapper.js' import { useShadowStyles } from '@map/composables/useShadowStyles.js' +import { SHADOW_BOX_SIZING_RESET } from './src/shadowReset.js' import ViewSelect from '@map/components/ViewSelect.vue' import ShareButton from '@map/components/map-controls/ShareButton.vue' // The SELECT VIEW strip is one of the sanctioned additions, so DX-life has no equivalent @@ -170,7 +171,7 @@ const SHARE_EMBED_PATH = 'regions-dashboard/regions-mfe/index.html' // NO BACKTICKS inside this CSS string, including in comments — one terminates the JS template // literal and the build stops compiling (house style). -useShadowStyles(PANE_GRID_STYLES + '\n' + +useShadowStyles(SHADOW_BOX_SIZING_RESET + PANE_GRID_STYLES + '\n' + // SELF-SIZE. Every layer inside is absolute/height:100% (.rmfe -> .pane-col -> .pane), so the // chain resolves to ZERO unless the HOST itself is sized — the "elements are not embedding" // failure upg-100-mfe documents. Declaring :host makes this fill whatever slot it is mounted diff --git a/plugin-designs/regions-dashboard/src/RegionCountryMap.vue b/plugin-designs/regions-dashboard/src/RegionCountryMap.vue index 774734f96..82c2a90de 100644 --- a/plugin-designs/regions-dashboard/src/RegionCountryMap.vue +++ b/plugin-designs/regions-dashboard/src/RegionCountryMap.vue @@ -739,7 +739,6 @@ useShadowStyles( // root so it is verified where it is measured; porting it bundle-wide and DELETING those seven // patches is carded (BUILD-PORT-THEIR-BOX-SIZING-RESET-TO-THE-SHADOW-ROOT) because it changes // geometry in components whose match against live has to be re-measured, not assumed. - '.rcm, .rcm *, .rcm *::before, .rcm *::after{ box-sizing:border-box; }' + '.rcm{ display:block; position:relative; width:100%; min-height:780px; aspect-ratio:16/9;' + ' overflow:hidden; border-radius:inherit; }' + '@media (max-width:768px){ .rcm{ min-height:0; aspect-ratio:1/2; } }' + diff --git a/plugin-designs/regions-dashboard/src/UupgsList.vue b/plugin-designs/regions-dashboard/src/UupgsList.vue index 16b93afbf..aecbd3729 100644 --- a/plugin-designs/regions-dashboard/src/UupgsList.vue +++ b/plugin-designs/regions-dashboard/src/UupgsList.vue @@ -326,7 +326,7 @@ useShadowStyles( // type at every other size. MEASURED: theirs renders ~600px inside the 1200px container // and shows the whole placeholder; ours truncated it at 480px. '.search-box{ position:relative; width:100%; max-width:50ch; margin-inline:auto; }' + - '.search-box input{ box-sizing:border-box; width:100%; border:1px solid #666; border-radius:100px;' + + '.search-box input{ width:100%; border:1px solid #666; border-radius:100px;' + ' background:var(--dx-white); padding:10px var(--spacing-lg); text-indent:1.5em; font:inherit;' + ' font-size:var(--font-size-md); color:var(--dx-text); }' + '.search-box input::placeholder{ color:gray; }' + @@ -397,7 +397,7 @@ useShadowStyles( '.grid{ display:grid; grid-gap:var(--spacing-2xl);' + ' grid-template-columns:repeat(auto-fit, minmax(min(400px,100%), 1fr)); }' + // .card + .uupg__card: their grid-template-areas, radius-xlg, --spacing-md padding + gap - '.uupg__card{ box-sizing:border-box; background:var(--dx-white); color:var(--dx-text);' + + '.uupg__card{ background:var(--dx-white); color:var(--dx-text);' + ' border-radius:35px; display:grid;' + ' grid-template-areas:"image header adopted" "image content content" "footer footer footer-right";' + ' grid-template-columns:auto 1fr auto; grid-template-rows:auto auto auto;' + @@ -424,7 +424,7 @@ useShadowStyles( '.uupg__match{ margin:0; font-size:var(--font-size-sm); color:var(--dx-muted); }' + // .button.compact, grid-area footer, bottom-aligned '.uupg__more-button{ grid-area:footer; align-self:flex-end; display:block; width:fit-content;' + - ' box-sizing:border-box; padding:10px var(--spacing-2xl); color:#f3f3f1; background:#92b195;' + + ' padding:10px var(--spacing-2xl); color:#f3f3f1; background:#92b195;' + ' border-radius:100px; border:1px solid transparent; line-height:1; text-decoration:none;' + ' text-transform:uppercase; font-size:var(--font-size-md);' + // THEIR FAMILY IS "Brandon Grotesque", WHICH IS COMMERCIAL — the one substitution RULE 1 diff --git a/plugin-designs/regions-dashboard/src/shadowReset.js b/plugin-designs/regions-dashboard/src/shadowReset.js new file mode 100644 index 000000000..11e6df297 --- /dev/null +++ b/plugin-designs/regions-dashboard/src/shadowReset.js @@ -0,0 +1,52 @@ +/** + * Their box-sizing reset, ported into the shadow root. + * + * WHAT THIS IS. app/assets/styles/base/_reset.scss:3-8 on DX-life master, verbatim: + * + * *, + * *::before, + * *::after { + * box-sizing: border-box; + * } + * + * a piccalil.li reset that applies to EVERY element on their page. + * + * WHY IT WAS MISSING FOR SO LONG, and why no component could reveal it. Our leaf document already + * carries the identical rule (regions-dashboard/index.html:9) -- but these components render inside + * a SHADOW ROOT, and a document-level `*` selector does not cross a shadow boundary. So every + * element in here was content-box while every element of theirs was border-box, and no scoped + * component style could show the difference. It was the SEVENTH global rule of theirs found + * missing, and it is why this codebase had accumulated seven separate per-element + * `box-sizing:border-box` patches: each was a local workaround for the reset that never arrived. + * + * WHAT IT COST, measured rather than described: their legend swatch is `width:10px; border:1px + * solid #fff` -- 10px under border-box, 12px under content-box. Two swatch columns of 1px each are + * exactly the 2px by which our legend panel measured wider than theirs (167px vs 165px) after the + * rest of the legend had been aligned to the pixel. The same label string measured 129px on both + * sides in the same Poppins, so the text was never the cause. + * + * WHY PORTING IT IS SAFE, and this is the part that made the change cheap instead of frightening: + * the blast radius was MEASURED before any of it was written. + * _probe-boxsizing-blast-radius.mjs injected this exact rule into every shadow root at runtime and + * diffed every element's rect before and after -- 0 of 431 elements moved on region-dashboards and + * 0 of 678 on regions-list, with 301 already border-box by their own declarations. So the port is a + * geometric NO-OP and the whole value of it is DELETING the per-element patches it replaces, which + * is the direction RULE 3 asks for. The detector was negative-tested (NEG=1 injects padding-left:3px + * and it reports the movement), because a zero from a check that cannot fail is worth nothing. + * + * WHY HERE AND NOT library/useShadowStyles.js. Putting it there would fix all 25 bundles at once and + * is tempting for exactly that reason -- but the blast radius above was measured on THESE two pages + * only, and geometry in 23 other bundles would change on an unmeasured basis. RULE 2 says default to + * the plugin folder; the second consumer that would justify library/ is not named yet. If another + * plugin wants it, it moves then, with that plugin's blast radius measured too. + * + * WHY A MODULE AND NOT THREE COPIES. Three roots inject into the shared shadow root + * (region-dashboards, regions-list, regions-mfe) and any of them can be the one that mounts, so all + * three need it. One exported string keeps it one fact in one place. + */ + +/** Their _reset.scss:3-8, as a shadow-root-scoped rule. */ +export const SHADOW_BOX_SIZING_RESET = + '*, *::before, *::after{ box-sizing:border-box; }' + +export default SHADOW_BOX_SIZING_RESET diff --git a/public/dx-maps/doxa-maps-spa/manifest.json b/public/dx-maps/doxa-maps-spa/manifest.json index fd3fa284e..496545596 100644 --- a/public/dx-maps/doxa-maps-spa/manifest.json +++ b/public/dx-maps/doxa-maps-spa/manifest.json @@ -888,7 +888,7 @@ "bundle": "regions-dashboard", "scriptUrl": "./regions-dashboard/regions-dashboard.js", "output": "dx-maps/regions-dashboard/regions-dashboard.js", - "integrity": "sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG", + "integrity": "sha384-FDwUoPdVCOpa/WplF4PpBVLYMusCFPZqbxBBqQOFlI0sJhR+UB4HuSbH/O4MJec0", "elements": [ "regions-dashboard" ], @@ -908,7 +908,7 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } }, { @@ -922,7 +922,7 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -936,7 +936,7 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ] diff --git a/public/dx-maps/manifest.json b/public/dx-maps/manifest.json index fd3fa284e..496545596 100644 --- a/public/dx-maps/manifest.json +++ b/public/dx-maps/manifest.json @@ -888,7 +888,7 @@ "bundle": "regions-dashboard", "scriptUrl": "./regions-dashboard/regions-dashboard.js", "output": "dx-maps/regions-dashboard/regions-dashboard.js", - "integrity": "sha384-z4GurUdJw60hce5cF5GM+ugsgNGMEYEdkT0VlBXI8XOw1J4NP+F5CUtoD7ahznVG", + "integrity": "sha384-FDwUoPdVCOpa/WplF4PpBVLYMusCFPZqbxBBqQOFlI0sJhR+UB4HuSbH/O4MJec0", "elements": [ "regions-dashboard" ], @@ -908,7 +908,7 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } }, { @@ -922,7 +922,7 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n" + "script": "\n\n" } }, { @@ -936,7 +936,7 @@ "scriptUrl": "./regions-dashboard/regions-dashboard.js", "embed": { "iframe": "", - "script": "\n\n\n\n" + "script": "\n\n\n\n" } } ] diff --git a/public/dx-maps/regions-dashboard/region-dashboards/index.html b/public/dx-maps/regions-dashboard/region-dashboards/index.html index 49c4c4c63..507e973c6 100644 --- a/public/dx-maps/regions-dashboard/region-dashboards/index.html +++ b/public/dx-maps/regions-dashboard/region-dashboards/index.html @@ -30,7 +30,7 @@ - + - + - +