MEASURED before: the region dashboard matched live doxa.life on 61 of 68 computed properties, and every one of the 7 differences traced to a single cause — Bebas Neue could not be loaded. Three were the font-family itself, and four were workarounds it forced: an uppercase hack imitating Bebas's caps, and two font-sizes stepped down because a wider fallback face overflowed their card widths. So it was never seven problems, it was one with six knock-ons. DX-life does not ship the file either — @nuxt/fonts downloads and self-hosts it for them, which is why their computed family reads "Bebas Neue", "Bebas Neue Fallback: ...". We could not @font-face from their origin: their file is served without CORS headers, so a cross-origin face fails silently. The font is a Google Font under the OFL, so it is vendored here from Google Fonts — both subsets, their own unicode-ranges (css2 v16) — self-hosted, never a CDN, the same rule public/assets/vendor/tabulator follows. library/composables/useDocumentFont.js is shared because @font-face is DOCUMENT-scoped: a face declared inside a shadow root is ignored, so a custom element cannot register its own webfont through useShadowStyles. That asymmetry is the reason this is a separate composable rather than a flag on that one. Idempotent per document, so a composite registers one face for both panes; both profiles call it because each has a standalone leaf page where the other is absent. With the real face loaded, the workarounds are DELETED rather than kept: the goal-card heading is back to their 59.6046px, the headline figure back to 59.6046px, and text-transform:uppercase and font-stretch:condensed are gone — Bebas is already caps and already condensed, and stacking those changed the glyphs. The three-across switcher still holds at 372/372/372 with no overflow, which is the geometry that forced the step-downs in the first place. ★ THE CATCH THAT MATTERED. With the font vendored the diff said 68 of 68 IDENTICAL, 0 differ — and it was WRONG. document.fonts reported our face in "error" status: generate-tree stages an ALLOWLIST of asset dirs into the tree, fonts/ was not in it, so the leaf resolved assets/fonts/... to a 404 and every heading silently rendered on the fallback. Both sides were agreeing on a FALLBACK, which is not a match. A font that 404s is invisible to any check that reads font-family instead of asking the engine whether the family is usable, so the probe now calls document.fonts.check() and the staging list carries fonts/. After the fix: usable true, face "loaded", and the headings render in Bebas. Two honest notes. The fallback TAIL still differs — theirs carries @nuxt/fonts' generated metric-fallback names we do not produce — so the probe compares the FIRST family (the face actually used) and reports the tail as an accepted difference rather than counting it either way. And document.fonts.check() returns false on THEIR page, which contradicts it visibly rendering in Bebas, so that check is trusted in one direction only: it proves ours loaded, it does not prove parity. The licence ships beside the font, extensionless: check:public-hygiene refuses *.txt in a served folder and is right to, while the OFL requires the licence to accompany redistributed font files. The conventional name satisfies both. |
||
|---|---|---|
| .. | ||
| api | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| i18n | ||
| layouts | ||
| maps | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| index.js | ||
| ProfileLoader.vue | ||
| README.md | ||
library/ (@map) — the shared map template
The other half of the architecture (see ../plugin-designs/README.md):
every map in plugin-designs/<your-map>/ composes the reusable, parameterized pieces that live here.
Fix something here and every map benefits on the next build; break something here and every map
breaks — read components/WARNING.md before touching shared pieces.
| Folder | What lives there |
|---|---|
ProfileLoader.vue |
The root of every bundle: parses profile-config, loads the profile, provides the stores + config |
components/ |
Shared UI — map, legends, controls, popup (PeopleGroupDetail), tables, charts, hints (README) |
composables/ |
Shared logic — map engine (useMapInstance), provider switch, shadow-DOM styles, theming |
stores/ |
Per-instance Pinia stores (ui / map / data) — provided by ProfileLoader, injected by children |
api/ |
Data-source registry (sources.json) + fetch layer — the JAMstack runtime wiring |
colors/ |
Color sets maps opt into via profile-config.colorSet |
i18n/ |
The shared string catalogs (all maps speak the same languages) |
data/ |
Small shared datasets baked at build time (big served data lives in ../data/) |
constants/ |
Shared constants |
scripts/ |
Maintainer pipelines (posters, research) — not part of any bundle |
The rule of thumb (same as WARNING.md): custom code in your app-profile, reusable code here — and things only move from the first to the second once they've earned it.