Skip to content

Embeds & white-label

Any newspaper, NGO, or government microsite can carry a real, cited Futuros figure — value plus a source chip that deep-links to the primary source — with a single <iframe>. The honesty contract travels with the embed: an embed never shows a number without its provenance.

Three widget routes plus a builder:

RouteRenders
/embed/statOne cited stat — big value + unit, signed delta chip, source chip, wordmark
/embed/chartThe same indicator as a small line chart (latest point labeled)
/embed/boardA responsive grid of several stat/chart panels — a whole dashboard in one iframe
/incrustarThe ES builder UI — pick options, preview live, copy the snippet

All embed routes are frameable cross-origin: vercel.json sets Content-Security-Policy: frame-ancestors * on /embed/(.*) (the rest of the site is X-Frame-Options: SAMEORIGIN). App chrome (nav, onboarding) no-ops under /embed, so the widget paints only itself.

/embed/stat and /embed/chart

Shared query parameters:

ParamValuesDefaultNotes
parampillar slug (salud, educacion, …)saludwhich pillar
isoISO3 (MEX, BRA, …) or LATAMMEXuppercased automatically; LATAM renders the regional aggregate ("América Latina y el Caribe")
indicatorindicator idfirst indicator of the cellpick a specific metric
themelight | darkdark
langes | en | ptesfull trilingual strings + localized metric labels/units
accenthex color #RGB#RRGGBBAAOxford blue (#11457e light / #6496d5 dark)white-label accent (must match /^#[0-9a-fA-F]{3,8}$/); overrides both the accent and the positive-delta color

The value, delta (vs. the 3-year mean), unit and source chip come from the same static cell payload the app uses; the source chip links to the indicator's primary source and the wordmark links back to the live atlas cell.

Four mechanics behind that paragraph (all in src/routes/_embed-shared.tsx):

  • Static-only gate. fetchParameter() loads the baked cell /data/parameter-cache/<param>__<ISO>.json and reports source: "static" | "mock" plus status: ready | not_generated | quarantined. On a cache miss the main app paints a deterministic mock to keep its layout alive; the embed treats anything that is not source:"static" — or is not_generated — as no data, because a mock value inside a third-party iframe would be a fabricated number wearing a real source chip.
  • Source chip resolution order. Three steps, first hit wins: inline fields on the indicator (source, source_url, vintage_year) → the payload's own citations[] row matched by citation_id → the source registry (inferSourceKey(citation_id) → institution metadata → deepLinkFor() URL template). The registry step is the same machinery as syntheticCitation(): the chip can be rebuilt from the id alone, and only an id that maps to no known institution yields nothing.
  • Labels. Baked payloads carry canonical ES labels/units only; ?lang=en|pt overlays them from the baked metrics registry, loaded async and cached module-wide. The ES text paints immediately and stays if that fetch never resolves, and the embed honours ?lang= exactly — unlike the main app it never falls back to the visitor's stored language preference.
  • Delta colour is direction-aware. The chip colours by the indicator's good_direction (up / down / neutral): a falling value of a down-is-good metric renders as good; neutral renders grey.

/embed/chart additionally requires a non-empty series on the indicator — a cited value with no history renders the no-data state, never an empty chart. The latest point is marked with a reference dot and repeated as the headline value.

Minimal stat embed:

html
<iframe
  src="https://futuros.xyz/embed/stat?param=salud&iso=MEX&theme=light&lang=en"
  width="320" height="180" style="border:0" loading="lazy"
  title="Futuros — Health, Mexico"></iframe>

White-labeled chart embed (custom accent, English, specific indicator):

html
<iframe
  src="https://futuros.xyz/embed/chart?param=educacion&iso=BRA&indicator=se_xpd_totl_gd_zs&theme=dark&lang=en&accent=%23004b87"
  width="360" height="240" style="border:0" loading="lazy"
  title="Futuros — Education spending, Brazil"></iframe>

Note the URL-encoded # (%23). Suggested sizes match the /incrustar builder: stat 320×180, chart 360×240.

/embed/board — multi-panel dashboard

One iframe renders a themed, responsive grid of stat/chart panels. A partner (IDB / CAF / CELAC) drops a single iframe and gets a whole dashboard, white-labeled with no code change.

Parameters:

ParamValuesDefaultNotes
panelscomma-separated kind:param:iso[:indicator]kind = stat | chart; up to 12 panels
themelight | darkdarkthemes the shell and every panel
accenthex colorpasses through to every panel
brandfree textFuturosreplaces the wordmark; with a custom brand the footer attribution reads "powered by Futuros ↗", otherwise it reads "futuros.xyz ↗"
langes | en | ptesforwarded to every panel

Each panel spec is kind:param:iso[:indicator] — e.g. stat:salud:MEX or chart:educacion:BRA:se_xpd_totl_gd_zs. The board mounts each panel as a same-origin /embed/stat or /embed/chart iframe, forwarding theme, lang and accent.

Parsing degrades per field, never whole-board: an unknown kind becomes stat, an empty param becomes salud, an empty iso becomes MEX (always uppercased), and anything past 12 panels is silently truncated. Panels render as fixed-height iframes (stat 160 px, chart 220 px) in an auto-fill grid with a 260 px minimum column, so the board reflows with the host page's width.

A white-labeled country dashboard:

html
<iframe
  src="https://futuros.xyz/embed/board?panels=stat:salud:MEX,chart:educacion:MEX,stat:seguridad:MEX,chart:trabajo-economia:MEX&theme=light&accent=%23004b87&brand=IDB&lang=en"
  width="100%" height="640" style="border:0" loading="lazy"
  title="Futuros dashboard — Mexico"></iframe>

If panels is empty the board shows a hint (?panels=stat:salud:MEX,chart:educacion:BRA) rather than a blank frame.

/incrustar — the builder

https://futuros.xyz/incrustar?lang=en is the no-code builder for the single-widget embeds. Pick pillar, country, indicator, type (stat/chart) and theme; it shows a live iframe preview of the real cited value and gives you a copy-paste one-line snippet. The indicator dropdown is populated from the selected cell's actual indicators, so you only ever embed a metric that exists.

Theming — themeTokens override semantics

Each widget inlines a complete CSS-variable set on its own root (themeTokens() in _embed-shared.tsx) — there is no global theme class, so host-page styles cannot bleed in and the widget cannot leak out:

TokenRole?accent= overrides it?
--e-bg / --e-panel / --e-lineCanvas, card, borderNo
--e-ink / --e-ink-2 / --e-ink-3Text hierarchyNo
--e-accentWordmark, chart line strokeYes
--e-goodGood-direction deltaYes — same hex as the accent
--e-badBad-direction delta, quarantine caveatNo — stays red
--e-citeCitation chipNo

The override is deliberately partial: accent replaces exactly --e-accent and --e-good, never --e-bad or --e-cite. A partner can brand the widget and its positive deltas; the warning colour and the citation chip are not brandable — you can white-label the number, not the caveat.

Honesty guarantees

The provenance contract has teeth inside the widget itself:

  • No fabricated numbers. An embed refuses to render mock/fallback payloads — if the cell's data source isn't the static corpus, it shows "Data not available" instead of a made-up figure.
  • Quarantine is visible. A cell flagged by the validators renders with an explicit "under review" caveat rather than silently presenting a doubted number.
  • The citation travels. The source chip is reconstructed from the citation_id alone (see Provenance), so the deep link works even though the embed never loads the full citation registry.

Framing & security

  • Embed routes send Content-Security-Policy: frame-ancestors * — embeddable in any host page.
  • All other routes send X-Frame-Options: SAMEORIGIN — the main app is not frameable off-origin.
  • Embeds are pure GETs of static data; there is no auth and nothing to submit.
  • Widgets are self-contained: theme is applied via inline CSS-variable overrides on the embed root (not a global class), so the same number reads correctly inside any host page.

The figures behind every embed are the same cited observations exposed by the Public API v1 — the embed is just a rendered, linkable view of one cell.

Every figure with its source — traceability is the contract.