Skip to content

Embeds y marca blanca

Cualquier periódico, ONG o micrositio gubernamental puede llevar una cifra real y citada de Futuros — el valor más un chip de fuente que enlaza en profundidad a la fuente primaria — con un único <iframe>. El contrato de honestidad viaja con el embed: un embed nunca muestra un número sin su procedencia.

Tres rutas de widget más un builder:

RutaRenderiza
/embed/statUna cifra citada — valor grande + unidad, chip de delta con signo, chip de fuente, wordmark
/embed/chartEl mismo indicador como un gráfico de líneas pequeño (con el último punto etiquetado)
/embed/boardUna cuadrícula responsiva de varios paneles stat/chart — un tablero completo en un solo iframe
/incrustarLa UI del builder en ES — elige opciones, previsualiza en vivo, copia el snippet

Todas las rutas de embed son enmarcables cross-origin: vercel.json fija Content-Security-Policy: frame-ancestors * en /embed/(.*) (el resto del sitio lleva X-Frame-Options: SAMEORIGIN). El chrome de la app (navegación, onboarding) se desactiva bajo /embed, así que el widget pinta únicamente su propio contenido.

/embed/stat y /embed/chart

Parámetros de query compartidos:

ParámetroValoresPor defectoNotas
paramslug de pilar (salud, educacion, …)saludqué pilar
isoISO3 (MEX, BRA, …) o LATAMMEXse pasa a mayúsculas automáticamente; LATAM renderiza el agregado regional ("América Latina y el Caribe")
indicatorid de indicadorprimer indicador de la celdaelige una métrica específica
themelight | darkdark
langes | en | ptestextos trilingües completos + etiquetas/unidades de métrica localizadas
accentcolor hex #RGB#RRGGBBAAazul Oxford (#11457e claro / #6496d5 oscuro)acento de marca blanca (debe cumplir /^#[0-9a-fA-F]{3,8}$/); sobrescribe tanto el acento como el color del delta positivo

El valor, el delta (frente a la media de 3 años), la unidad y el chip de fuente salen del mismo payload estático de celda que usa la app; el chip de fuente enlaza a la fuente primaria del indicador y el wordmark enlaza de vuelta a la celda viva del atlas.

Cuatro mecánicas detrás de ese párrafo (todas en src/routes/_embed-shared.tsx):

  • Compuerta solo-estático. fetchParameter() carga la celda horneada /data/parameter-cache/<param>__<ISO>.json y reporta source: "static" | "mock" más status: ready | not_generated | quarantined. Ante un fallo de caché, la app principal pinta un mock determinista para mantener vivo su layout; el embed trata todo lo que no sea source:"static" — o que sea not_generated — como sin datos, porque un valor mock dentro de un iframe de terceros sería un número fabricado vistiendo un chip de fuente real.
  • Orden de resolución del chip de fuente. Tres pasos, gana el primer acierto: campos inline en el indicador (source, source_url, vintage_year) → la fila de citations[] del propio payload emparejada por citation_id → el registro de fuentes (inferSourceKey(citation_id) → metadatos de la institución → plantilla de URL de deepLinkFor()). El paso del registro es la misma maquinaria que syntheticCitation(): el chip puede reconstruirse a partir del id solo, y únicamente un id que no mapea a ninguna institución conocida no produce nada.
  • Etiquetas. Los payloads horneados llevan solo etiquetas/unidades canónicas en ES; ?lang=en|pt las superpone desde el registro horneado de métricas, cargado de forma asíncrona y cacheado a nivel de módulo. El texto en ES se pinta de inmediato y permanece si ese fetch nunca resuelve, y el embed respeta ?lang= al pie de la letra — a diferencia de la app principal, nunca recae en la preferencia de idioma almacenada del visitante.
  • El color del delta es consciente de la dirección. El chip se colorea según el good_direction del indicador (up / down / neutral): un valor a la baja en una métrica donde bajar es bueno se renderiza como bueno; neutral se renderiza en gris.

/embed/chart exige además una series no vacía en el indicador — un valor citado sin historial renderiza el estado sin datos, nunca un gráfico vacío. El último punto se marca con un punto de referencia y se repite como valor titular.

Embed mínimo de stat:

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

Embed de gráfico con marca blanca (acento personalizado, en inglés, indicador específico):

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>

Nota el # codificado en URL (%23). Los tamaños sugeridos coinciden con el builder /incrustar: stat 320×180, chart 360×240.

/embed/board — tablero multipanel

Un solo iframe renderiza una cuadrícula tematizada y responsiva de paneles stat/chart. Un socio (BID / CAF / CELAC) inserta un único iframe y obtiene un tablero completo, con marca blanca y sin cambiar código.

Parámetros:

ParámetroValoresPor defectoNotas
panelslista separada por comas kind:param:iso[:indicator]kind = stat | chart; hasta 12 paneles
themelight | darkdarktematiza el contenedor y cada panel
accentcolor hexse propaga a cada panel
brandtexto libreFuturosreemplaza el wordmark; con un brand personalizado la atribución del pie dice "powered by Futuros ↗", en caso contrario dice "futuros.xyz ↗"
langes | en | ptesse reenvía a cada panel

Cada especificación de panel es kind:param:iso[:indicator] — p. ej. stat:salud:MEX o chart:educacion:BRA:se_xpd_totl_gd_zs. El board monta cada panel como un iframe same-origin de /embed/stat o /embed/chart, reenviando theme, lang y accent.

El parseo se degrada por campo, nunca a nivel del board completo: un kind desconocido se convierte en stat, un param vacío en salud, un iso vacío en MEX (siempre en mayúsculas), y todo lo que pase de 12 paneles se trunca en silencio. Los paneles se renderizan como iframes de altura fija (stat 160 px, chart 220 px) en una cuadrícula auto-fill con columna mínima de 260 px, de modo que el board se reacomoda con el ancho de la página anfitriona.

Un tablero de país con marca blanca:

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=es"
  width="100%" height="640" style="border:0" loading="lazy"
  title="Tablero Futuros — México"></iframe>

Si panels viene vacío, el board muestra una pista (?panels=stat:salud:MEX,chart:educacion:BRA) en lugar de un marco en blanco.

/incrustar — el builder

https://futuros.xyz/incrustar es el builder sin código para los embeds de widget individual. Elige pilar, país, indicador, tipo (stat/chart) y tema; muestra una previsualización en vivo en iframe del valor citado real y te entrega un snippet de una línea listo para copiar y pegar. El desplegable de indicadores se puebla con los indicadores reales de la celda seleccionada, así que solo puedes incrustar una métrica que existe.

Tematización — semántica de sobrescritura de themeTokens

Cada widget inserta inline un conjunto completo de variables CSS en su propia raíz (themeTokens() en _embed-shared.tsx) — no hay clase de tema global, así que los estilos de la página anfitriona no pueden filtrarse hacia dentro y el widget no puede fugarse hacia fuera:

TokenRol¿?accent= lo sobrescribe?
--e-bg / --e-panel / --e-lineLienzo, tarjeta, bordeNo
--e-ink / --e-ink-2 / --e-ink-3Jerarquía de textoNo
--e-accentWordmark, trazo de la línea del gráfico
--e-goodDelta en dirección buena — el mismo hex que el acento
--e-badDelta en dirección mala, salvedad de cuarentenaNo — se mantiene rojo
--e-citeChip de citaNo

La sobrescritura es deliberadamente parcial: accent reemplaza exactamente --e-accent y --e-good, nunca --e-bad ni --e-cite. Un socio puede poner su marca al widget y a sus deltas positivos; el color de advertencia y el chip de cita no admiten marca — puedes poner marca blanca al número, no a la salvedad.

Garantías de honestidad

El contrato de procedencia tiene dientes dentro del propio widget:

  • Sin números fabricados. Un embed se niega a renderizar payloads mock/de respaldo — si la fuente de datos de la celda no es el corpus estático, muestra "Datos no disponibles" en lugar de una cifra inventada.
  • La cuarentena es visible. Una celda marcada por los validadores se renderiza con una salvedad explícita de "en revisión" en lugar de presentar en silencio un número puesto en duda.
  • La cita viaja. El chip de fuente se reconstruye a partir del citation_id solo (ver Procedencia), así que el enlace profundo funciona aunque el embed nunca cargue el registro completo de citas.

Enmarcado y seguridad

  • Las rutas de embed envían Content-Security-Policy: frame-ancestors * — incrustables en cualquier página anfitriona.
  • Todas las demás rutas envían X-Frame-Options: SAMEORIGIN — la app principal no es enmarcable fuera de su origen.
  • Los embeds son GETs puros de datos estáticos; no hay autenticación ni nada que enviar.
  • Los widgets son autocontenidos: el tema se aplica mediante sobrescrituras inline de variables CSS en la raíz del embed (no una clase global), así que el mismo número se lee correctamente dentro de cualquier página anfitriona.

Las cifras detrás de cada embed son las mismas observaciones citadas que expone la API Pública v1 — el embed es solo una vista renderizada y enlazable de una celda.

Cada cifra con su fuente — la trazabilidad es el contrato.