Map cards keep rounded corners and phones get a taller map slot; old /js/doxa-maps links 301 to the dx-maps tree (carried from doxa-maps-build)

This commit is contained in:
doxa-maps 2026-09-28 10:37:32 +00:00
parent c1054993c6
commit ff1e2dcbe4
3 changed files with 74 additions and 3 deletions

View file

@ -56,10 +56,19 @@
position: relative;
overflow: hidden;
border-radius: var(--border-radius-lg);
// An <iframe> is composited in its own layer: `overflow: hidden` + `border-radius` on the
// parent does not clip it in Chrome until the layer tree settles after the frame paints,
// so the map showed square corners until it loaded and rounded ones after. clip-path is
// applied at paint time regardless of compositing, and the iframe carries the radius too.
clip-path: inset(0 round var(--border-radius-lg));
isolation: isolate;
iframe {
display: block;
width: 100%;
height: 20rem;
border: 0;
border-radius: inherit;
}
.overlay {

View file

@ -78,19 +78,63 @@ const effectiveConfig = computed(() => {
}
@keyframes doxa-map-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .doxa-map-spinner { animation: none; } }
/* ROUNDED MAP CARDS (carried from doxa-maps-build). The slot is a LAYOUT, not a mask: the map sits
inside an opaque rounded frame, inset by enough (0.32r, just over the r(1-1/sqrt 2) corner bite) that its
own square corner never reaches the rounded edge, and the four corner wedges are PAINTED over it on
their own layer. A WebGL canvas is a composited layer that an ancestor's radius does not reliably clip
(the corners went square once a map loaded), so clipping alone is not enough. */
.doxa-map-slot {
/* read by the maps inside their shadow DOM, to round the canvas itself */
--map-radius: var(--slot-radius, 0px);
--map-radius-corners: var(--slot-radius, 0px);
--slot-inset: calc(var(--slot-radius, 0px) * 0.32);
--slot-radius: 0px;
background: var(--slot-corner-bg, var(--color-surface-default, #F3F3F1));
display: block;
position: relative;
width: 100%;
min-height: 780px;
aspect-ratio: 16 / 9;
overflow: hidden;
/* clip-path is applied at paint time, so the corners are round from the first frame, before the
compositor has a mask layer for the canvas */
clip-path: inset(0 round var(--slot-radius));
isolation: isolate;
will-change: transform;
transform: translateZ(0);
}
.doxa-map-slot::after {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
z-index: 4;
/* own compositing layer, so the map's GPU layer cannot paint over the wedges */
transform: translateZ(0);
will-change: transform;
backface-visibility: hidden;
--corner-tl: var(--slot-radius, 0px);
--corner-tr: var(--slot-radius, 0px);
--corner-br: var(--slot-radius, 0px);
--corner-bl: var(--slot-radius, 0px);
--corner-bg: var(--slot-corner-bg, var(--color-surface-default, #F3F3F1));
background:
radial-gradient(circle at 100% 100%, #0000 calc(var(--corner-tl) - 0.5px), var(--corner-bg) var(--corner-tl)) left top / var(--corner-tl) var(--corner-tl) no-repeat,
radial-gradient(circle at 0 100%, #0000 calc(var(--corner-tr) - 0.5px), var(--corner-bg) var(--corner-tr)) right top / var(--corner-tr) var(--corner-tr) no-repeat,
radial-gradient(circle at 0 0, #0000 calc(var(--corner-br) - 0.5px), var(--corner-bg) var(--corner-br)) right bottom / var(--corner-br) var(--corner-br) no-repeat,
radial-gradient(circle at 100% 0, #0000 calc(var(--corner-bl) - 0.5px), var(--corner-bg) var(--corner-bl)) left bottom / var(--corner-bl) var(--corner-bl) no-repeat;
}
/* one corner size for every map card; the element's own radius is pinned to the same token */
.doxa-map-slot.rounded-md,
.doxa-map-slot.rounded-xlg { --slot-radius: var(--border-radius-lg); border-radius: var(--slot-radius); }
@media (max-width: 768px) {
.doxa-map-slot {
min-height: 0;
/* twice as tall as wide (80px more map than the 9/16 before it), capped at one screen so the page
below stays reachable */
aspect-ratio: 1 / 2;
max-height: 90svh;
}
}
@ -98,9 +142,13 @@ const effectiveConfig = computed(() => {
.doxa-map-slot :deep(doxa-research-map) {
display: block;
position: absolute;
inset: 0;
width: 100%;
height: 100%;
/* inset inside the rounded frame; the map keeps its stacking to itself so its legend and search
(z-index 1000+) cannot paint over the corner wedges */
inset: var(--slot-inset, 0px);
isolation: isolate;
z-index: 0;
width: auto;
height: auto;
/* Inherit any border-radius the host page gave the slot (e.g. .rounded-md /
.rounded-xlg on the research page). Without this, the bare custom element
paints a rectangular background during the brief window between page

View file

@ -61,6 +61,10 @@ const termsRouteRules = Object.fromEntries(
])
)
// The dx-maps tree the site loads maps from: the same value as runtimeConfig.public.dxMapsBase below, read
// at build time for the redirect rules.
const DX_MAPS_TREE = ((process.env.NUXT_PUBLIC_DX_MAPS_BASE ?? 'https://maps-dev.nuxtbase.app/cdn/dx-maps').replace(/\/+$/, '')) || '/dx-maps'
export default defineNuxtConfig({
// Embeddables (micro-frontends + web-components) have their own Vite builds —
// Nuxt must not watch or scan their source trees.
@ -162,6 +166,16 @@ export default defineNuxtConfig({
},
routeRules: {
// OLD MAP LINKS HEAL THEMSELVES (carried from doxa-maps-build): the dx-maps tree used to be served from
// this site at /js/doxa-maps/, then /js/doxa-maps-build/ and /embed/doxa-maps-build/. Bookmarks and older
// partner embeds 301 to the same path in the tree the site uses now (NUXT_PUBLIC_DX_MAPS_BASE, the maps
// CDN by default; this site's own /dx-maps when that is set empty). Bundles that no longer exist in the
// tree 404 there, as they would here.
'/js/doxa-maps/**': { redirect: { to: `${DX_MAPS_TREE}/**`, statusCode: 301 } },
'/js/doxa-maps-build/doxa-maps/**': { redirect: { to: `${DX_MAPS_TREE}/**`, statusCode: 301 } },
'/js/doxa-maps-build/assets/**': { redirect: { to: `${DX_MAPS_TREE}/assets/**`, statusCode: 301 } },
'/embed/doxa-maps-build/doxa-maps/**': { redirect: { to: `${DX_MAPS_TREE}/**`, statusCode: 301 } },
'/embed/doxa-maps-build/assets/**': { redirect: { to: `${DX_MAPS_TREE}/assets/**`, statusCode: 301 } },
// Content-hashed build assets never change under the same URL — cache forever.
'/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } },
// Fonts/images use stable filenames, so avoid `immutable`: a 7-day cache