The gallery is going onto the DX-life research page, and nothing had tried it on a page it didn't generate. From a second origin, a bare <script> + <doxa-maps-spa> drew over the host's header, and 8 of 13 tabs showed an error card. Three causes, each fixed in the shell: - No :host box. The layout root is position:absolute, so it anchored to the viewport instead of the element. :host is now a positioned block with a default height the host can override. - A foreign page sets none of the globals a leaf page bakes. Every data call hit the host's own origin and got HTML back. The manifest now publishes a `runtime` block (apiBaseUrl + the public token every leaf already carries). The shell fills window.MAP_APP_API_URL / window.MAP_TOKEN only when the page hasn't set them, and forwards tk in profile-config. - Nested elements resolved assets to <tree>/../assets, a sibling pool the publisher doesn't ship (404 live). The shell now forwards an absolute <tree>/assets. This also fixes the iframe mode, where Cities with Internet and Gospel Resources were failing the same way. Also adds a `tab` attribute (plus ?tab=) to deep-link into one map, and a `tab-change` event for the host. check:gallery-embed drives every tab in both modes from a second origin. It passes here and fails on the previous tree (7 findings). EMBED.md documents the contract. The Traefik template now sends CORS for the whole tree: live manifest.json answers without Access-Control-Allow-Origin.
3.3 KiB
Embedding the Research Gallery
<doxa-maps-spa> (labeled Research Gallery) is the capability-5 app shell: one element whose
tab rail nests every production map. It is built to drop onto somebody else's page. Below,
TREE means the URL of the built doxa-maps/ folder wherever it is served, for example
https://YOUR-HOST/doxa-maps.
Script embed (recommended)
<script src="TREE/doxa-maps-spa/doxa-maps-spa.js"></script>
<doxa-maps-spa></doxa-maps-spa>
That is the whole embed. The element finds its tree from its own <script> URL, reads
TREE/manifest.json, and takes the data API and public map token from the manifest's
runtime block. It sizes itself as a block that is 85vh tall (minimum 480px) and lays out
inside its own box. To change the height, either:
<doxa-maps-spa style="height: 900px"></doxa-maps-spa>
<!-- or, from your stylesheet -->
<style> doxa-maps-spa { --doxa-maps-spa-height: 70vh; } </style>
iframe embed
<iframe src="TREE/doxa-maps-spa/doxa-maps-spa/index.html"
style="width:100%; height:85vh; border:0" allow="fullscreen"></iframe>
Add ?tab=<id> to the src to open a specific map.
Attributes (all optional)
| Attribute | Default | What it does |
|---|---|---|
tab |
device default (Big Picture on desktop, Simple Research on phones) | Tab id to open first. Changing it later switches tabs. ?tab= on the page URL works too. |
language |
parent page / saved choice | UI language pushed to every tab (en, es, fr, …). |
api-base-url |
manifest runtime.apiBaseUrl |
Data API. Only applied when the page has not already set window.MAP_APP_API_URL. |
tk |
manifest runtime.token |
Mapbox public token. Only fills window.MAP_TOKEN when the page has not set one. |
token-url |
none | Token endpoint forwarded to each map. |
tree-base |
derived from the script URL | Pin the tree root explicitly (needed only when the script is re-hosted apart from its tree). |
heading |
DX-Maps |
Shell title (shown only when the shell header is enabled). |
Tab ids: dashboard-plus-grid (Big Picture), research-map-plus-grid (Simple Research),
regions-dashboard, upg-100, prayer-map (Prayer Progress), cities-internet,
gospel-resources, and every Experimental bundle by its bundle name.
Events
The element dispatches tab-change whenever the visible map changes, so the host page can
mirror it into its own URL or analytics:
document.querySelector('doxa-maps-spa')
.addEventListener('tab-change', (e) => console.log(e.detail[0].id, e.detail[0].label))
What the server must send
Both headers are required on a host other than the one serving the tree:
Access-Control-Allow-Origin: *on the whole tree, includingmanifest.jsonat its root andassets/. The gallery and its mapsfetch()these after mounting, andfetch()enforces CORS; a<script>tag alone does not. Ifmanifest.jsonlacks the header, every tab shows "Could not load …".Content-Security-Policy: frame-ancestors *ondoxa-maps-spa/doxa-maps-spa/index.html, for the iframe embed only.
serving/traefik-embed-headers.yaml has a ready rule for both.
Prove it
bun run check:gallery-embed # serves the built tree + a foreign host page, drives every tab in both modes