Maps: download their three scripts together, and show a spinner until the map is ready

The home, pray and adopt maps ran Mapbox GL, the geocoder and the map bundle one after
another, so on a cold first visit the slot stayed empty while ~1.8 MB downloaded in series.
Preload hints now fetch all three in parallel (they still run in order), and the slot shows a
quiet spinner (aria-busy) until the map element is defined.
This commit is contained in:
doxa-maps 2026-09-26 02:56:34 +00:00
parent aeb61e64f7
commit a61b1e810a
2 changed files with 37 additions and 4 deletions

View file

@ -21,6 +21,14 @@ useDoxaMap(props.bundle)
const tagName = props.bundle === 'research-map' ? 'doxa-research-map' : 'doxa-map'
const isSimple = props.bundle === 'simple-map'
/* Until the map's element is defined (its scripts are still downloading on a cold first visit),
* the slot would be an empty box. Show a quiet spinner instead, so the wait reads as loading. */
const ready = ref(false)
onMounted(() => {
if (typeof customElements === 'undefined') return
customElements.whenDefined(tagName).then(() => { ready.value = true })
})
/* With no Mapbox token configured (e.g. a fresh checkout with no .env), the
* map would refuse to start ("No Mapbox access token provided"). Ask the
* dx-maps bundle for its keyless MapLibre engine instead, as the Research
@ -36,7 +44,8 @@ const effectiveConfig = computed(() => {
</script>
<template>
<div class="doxa-map-slot">
<div class="doxa-map-slot" :aria-busy="ready ? undefined : 'true'">
<div v-if="!ready" class="doxa-map-loading" aria-hidden="true"><span class="doxa-map-spinner" /></div>
<component :is="tagName" :id="mapId" :profile-config="effectiveConfig" />
<div v-if="isSimple" class="doxa-map-scroll-pad doxa-map-scroll-pad--top" />
<slot />
@ -44,6 +53,24 @@ const effectiveConfig = computed(() => {
</template>
<style scoped>
.doxa-map-loading {
position: absolute;
inset: 0;
display: grid;
place-items: center;
background: #f2f2f0;
z-index: 1;
}
.doxa-map-spinner {
width: 36px;
height: 36px;
border-radius: 50%;
border: 3px solid rgba(59, 70, 61, 0.2);
border-top-color: rgba(59, 70, 61, 0.8);
animation: doxa-map-spin 0.9s linear infinite;
}
@keyframes doxa-map-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .doxa-map-spinner { animation: none; } }
.doxa-map-slot {
display: block;
position: relative;

View file

@ -55,15 +55,21 @@ function loadScript(src: string): Promise<void> {
export function useDoxaMap(bundle: BundleKey = 'simple-map') {
const config = useRuntimeConfig()
const MAP_APP_JS = BUNDLES[bundle]
useHead({
link: [
{ rel: 'stylesheet', href: 'https://api.mapbox.com/mapbox-gl-js/v3.24.0/mapbox-gl.css' },
{ rel: 'stylesheet', href: 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css' }
{ rel: 'stylesheet', href: 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.css' },
// The three scripts below still RUN one after another (the map bundle must find
// window.mapboxgl), but nothing stops them DOWNLOADING together: without these hints a cold
// first visit fetched ~1.8 MB of Mapbox GL, then the geocoder, then the map bundle, in series.
{ rel: 'preload', as: 'script', href: MAPBOX_JS },
{ rel: 'preload', as: 'script', href: MAPBOX_GEOCODER_JS },
{ rel: 'preload', as: 'script', href: MAP_APP_JS }
]
})
const MAP_APP_JS = BUNDLES[bundle]
onMounted(async () => {
try {
// Tell the map bundle which prayer API to fetch from. Must be set before