TWO SHARED-COMPONENT DEFECTS, both routed to me, both a step away from where the routing pointed. THE 14 NAMELESS BUTTONS ARE ONE COMPONENT. check:a11y-button-names reported 14 visible buttons with no accessible name across two country-posters leaves. The routing named countries-map.vue, which contains exactly one <button> and it has visible text — the gate prints the LEAF, so the file was an inference. Running the gate and then a probe that prints what a developer can grep for — class, innerHTML, box size, the custom-element host chain — named the real one: library/components/legends/SemanticTreeLegend.vue:1228, seven <button class="stl-caret">, 14x14, a 9x9 chevron and nothing else. The 14 is those 7 counted twice, because country-posters-mfe composes countries-map; worth saying, or the next reader hunts a second defect that does not exist. "(no title either)" in the gate's output is the diagnostic half: it rules out the title-only control this gate was built for, and it rules out Mapbox's own controls, which ship aria-labels. These were ours and they were empty — assistive technology said "button" once per expandable branch. Fixed at the source, which is why it lives in library/: an aria-label naming the branch the caret discloses, and aria-expanded, because a name tells a screen-reader user what the control is for and still not whether the branch is open. Three keys across eleven locales rather than a hardcoded label. And the same defect one element over, latent: .stl-x-chip carried only a `title` — precisely what this gate exists to reject — and hardcoded English besides. It escaped the sweep because it renders only while a row is selected. Seven surfaces render that legend, so one fix reaches all of them. The artifacts are per-bundle though: country-posters is rebuilt here because its cards are routed to me, and my own bundle with it. The other five stay stale until their owners rebuild, and I have not touched them. AND THE PIN HINT HAD NEVER BEEN TRANSLATED. The ask was to audit eleven locales for a stale "red dot". There is nothing stale, because there is nothing: no locale file contains "Tap a", "pinHint" or "coloured dot"; PinHint.vue imports no useI18n; the sentence is a JavaScript string literal. So every map that renders the hint has been showing an English sentence to readers in Arabic, Russian and Chinese. Which is worth noticing about the fix that came before it. The colour half was found and fixed carefully — the comment above it records the Driver reporting it five or more times, and why a shared component must not name a colour it cannot know. All correct. Nobody asked whether the sentence was in the catalogue. Same shape as the Russian headings next door: wrong only for readers who do not file bug reports in English, and so invisible to everyone who could. Eleven locales, every one saying coloured and never red. The pinHintLabel override still comes first, so a profile that does want to name its colour can. And the key falls back to the English sentence when a locale lacks it, so a missing translation degrades to today's behaviour rather than rendering the literal "messages.pinHint.tapDot" — the failure mode of a naive t() swap. Flagged and not taken, because they are shared components with owners: FilterDropdown renders a hardcoded English "Clear selection" and "No options found" (the latter already exists translated in the regions catalogue, so the component is ignoring a string that is right there), and PosterWall's export modal renders its reassurance line untranslated. |
||
|---|---|---|
| .. | ||
| api | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| i18n | ||
| layouts | ||
| maps | ||
| packet | ||
| 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.