VisionFunnel, LifeCalendar and ShareCalendar, all props-driven, no hardcoded list, print first: fixed px on the packet's 816x1056 canvas, nothing revealed by hovering, and the packet's own ink rather than a new palette, so a reader cannot tell which page came from the component and which from the renderer. The funnel draws steps as stacked trapezoids and overlays as brackets BESIDE them, never inside. Being inside a step is precisely the claim an overlay cannot make: 362 of DOXA's 2,107 groups are not frontier at all. It asks overlayPlacement whether an overlay is a slice rather than deciding, and prints each overlay's parts as text, because a bracket nobody can hover over explains nothing. Given steps that do not nest, or that mix publishers, it refuses and prints why instead of drawing a plausible picture. It is also not mirrored under RTL, which is worth saying because the obvious reading of "mirror it" is wrong here: the triangle is vertically directional, so a horizontal flip reverses nothing meaningful and inverts any text inside it. What mirrors is the label and bracket side, so those use logical properties. Two decisions now live in prop declarations, and a default is how either dies quietly. ShareCalendar refuses to name its own metric: the shipped figure is the evangelical share while the brief says "% Christian", and one default would reintroduce the confusion this project already retracted a P0 over, with nothing looking wrong. LifeCalendar's anchor defaults to null because the packet prints for a reader we do not know and shading their life is a guess presented as fact. Both are asserted through the SFC and JS parsers rather than a grep, since a regex cannot tokenise JavaScript, and a sibling prop that does carry a default is the negative control proving the arm is not vacuous. Adding default: '% Christian' turns it red, which I checked rather than assumed. |
||
|---|---|---|
| .. | ||
| api | ||
| colors | ||
| components | ||
| composables | ||
| constants | ||
| data | ||
| i18n | ||
| layouts | ||
| lists | ||
| maps | ||
| packet | ||
| packets | ||
| scripts | ||
| stores | ||
| styles | ||
| utils | ||
| vendor/cldr | ||
| 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.