Somos Valientes — canvas del camino (decisión de diseño e interacción)
Fecha: 2026-07-10
Contexto: Rediseño de portafolio. Rescatar la idea “Somos Valientes” (proyecto de
Lydia Cacho, educación para la paz) como pieza de muestra. Referencia viva:
somosvalientes.mx (quedó en sitio vertical simple). Fuente de verdad del diseño:
~/Desktop/SV/boceto.ai (50 artboards, AI 2018).
Entregable: ~/Desktop/SV/site/ — prototipo html/css/js corriendo y deployable.
Idea central
Lienzo 2D navegable en todos sentidos. Las escenas del libro/proyecto viven dispersas en un mundo grande, unidas por un sendero punteado que recorre un avión de papel. El usuario “sigue el camino a ser valiente”. El mapa de México es el landmark (escena “Empoderando”) con pins a ONGs reales: Marabunta, Escuela de niñas ciegas, Melel Xojobal. Esto es literal lo que el boceto proponía y no se logró.
Decisiones técnicas (devsigner: scope down, open source, local-first)
- DOM + SVG + GSAP, NO WebGPU en v1. WebGPU = soporte irregular y sobre-ingeniería para una demo. La capa de fondo (doodles/mapa) queda como punto de inserción si algún día se quieren shaders — sin reescribir la capa DOM.
- GSAP 3 (Draggable/Inertia/MotionPath) — gratis y open desde 2024. Vendorizado local
en
vendor/(137 KB). Mueve avión sobre el path + viaje de cámara entre escenas. - Paneo/inercia/rueda hechos a mano (sin lib). Cámara = punto de mundo en el centro
del viewport;
translate3dsobre#world. - Sendero derivado, no dibujado:
SCENES[](coords) → Catmull-Rom→bézier genera el path, el minimapa y las fracciones de viaje del avión. Editar una escena = editar el array. - Sin build. Deploy = arrastrar
site/a Netlify.
Swaps antes de público
- Fuente Geomanist (licencia de pago, tuya) → OFL geométrica (General Sans / Hanken Grotesk).
@font-faceaislado. - Silueta de México estilizada (a mano) → GeoJSON/TopoJSON proyectado si se quiere precisión.
- Video/formularios/fichas ONG = placeholders.
Escenas (orden del camino)
Portada · Sobre el proyecto · Tráiler · Conociendo a los valientes · La Unión hace la fuerza · ¿Cómo le hacen los valientes? · Soy Valiente · Empoderando (mapa MX) · Contacto y ayuda.
Estado — publicable (nivel alto)
Verificado en preview: intro-gate, viaje avión+cámara, temas claro/oscuro/amarillo/rojo, mapa con silueta real de MX + 5 pins numerados (CDMX/Chiapas/Jalisco/Sinaloa/Yucatán) + ruta punteada, ficha de aliada al tocar pin, minimapa, parallax de doodles, teclado, reduced-motion, touch.
Pase de craft (skill frontend-craft, thetasteskill.dev):
- Firma: mundo = cuaderno de cuadrícula (ancla escuela/infancia), no crema genérico.
- Avatar del recorrido: avión rojo del boceto (Nef pidió regresar del intento mochila); flip horizontal según dirección de viaje para no volar de cabeza.
- Camino claro: dashes grandes rojos + halo blanco.
- Cajas de cada paso: CTA con
.btn--line(pill de contorno que hereda la tinta de la tarjeta). Fix: antes usaba.btn--ghost(círculo fijo 44px) que rompía el texto en vertical. - Piso de calidad: focus visible, meta/OG/favicon, responsive móvil, escena activa resaltada.
- Publicable sin licencia: Hanken Grotesk (OFL) vendorizada como respaldo de Geomanist.
- Corte “una pieza menos”: doodle ‘star’ eliminado (fuera del brief).
Siguiente (witness > finished)
Subir a Netlify esta semana y enseñárselo a alguien (¿colega de R&D? ¿la propia gente del proyecto?). No dejarlo “en progreso” entre sesiones.
Pivote final (mismo día): dos actos
Nef pidió reestructura: Acto 1 = narrativa vertical con fade-ins (IntersectionObserver, scroll-snap); Acto 2 = “El mapa de los viajes” limpio. Orden del viaje: CDMX → Chiapas → Jalisco → Sinaloa → Yucatán, con el copy real de los 5 episodios. Avión vuela SOBRE la ruta curva exacta (Catmull-Rom; getPointAtLength → getScreenCTM → pantalla) con power2.inOut + inclinación tangente clampeada ±16° + settle al llegar. Bug resuelto: transición CSS de mapact nunca disparaba → opacidad por GSAP inline. Lección de verificación: pestaña oculta congela rAF/ticker GSAP → tweens en progress 0; verificar manejando tween.progress() a mano; posicionamiento de entrada ahora síncrono (offsetWidth flush, no rAF).
Mapa rehecho desde boceto 2.pdf (extracción vectorial real)
pdftocairo -svg sobre boceto 2 (pág. 9) → extraídos: continente naranja completo
(assets/land.svg, 2 paths, sangrado a los 4 bordes), avión detallado del boceto
(assets/plane.svg, 43 paths con orden de apilado: hélice/piloto/ruedas/carita) y la forma
del pin (PIN_D inline en main.js, tip normalizado al origen). Camino gris de dashes grandes
simulado. Espacio de coords = página del boceto (725.5×408). Pins por color de categoría
(#BA1B73 CDMX, 2DA44C Chiapas, 316CBD Jalisco, EB1658 Sinaloa, 1A2747 Yucatán) +
callout estilo pág. 10 (thumb con monito + “Estado — Org”) que aparece al aterrizar y se
voltea cerca del borde. El avión del boceto mira a la izquierda → espejo al volar a la derecha.
Portrait usa meet (mapa completo), landscape slice (sangrado). Gotcha: appendChild de
nodos de otro documento SVG falla silencioso → document.importNode(p,true); y el browser
cachea main.js aunque el server sirva fresco → ?v=N en index.html.
Fuentes de extracción conservadas en assets/map9.svg y map10.svg por si Nef quiere más piezas.
Modo oscuro del mapa (cierre)
Mar negro (—night), continente naranja intacto, camino gris oscuro 3A342E, panel/controles en la paleta del acto 1. Lo que Nef veía (“no se ve el fondo”) era el main.js viejo cacheado por su navegador (previo al fix importNode) — resuelto con versionado ?v=N; un hard-refresh (Cmd+Shift+R) la primera vez.
Nivel detalle con zoom (cierre de página)
Botón “Ver el detalle” en el panel → zoom cinematográfico GSAP: overview escala ×5 hacia el pin y se desvanece mientras aparece el nivel detalle = 32 estados de México en línea amarilla (#E9C63F, vector-effect:non-scaling-stroke) sobre negro — la pantalla “Nombre del Valiente” del boceto. Pin del episodio + etiqueta roja (org / estado · episodio) en el centroide del estado. Sin librería de tiles: GeoJSON de estados (angelnmara/geojson) vendorizado → proyectado a assets/states.svg (91 KB) con python (equirectangular + simplificación 1.2px). Zoom = animación de viewBox (AttrPlugin de GSAP core). ”‹ Volver al mapa” / Esc regresan; navegar episodios sale del detalle primero. Verificado ida/vuelta, 0 errores. v=8.
Afinado de pins + interacción de marcador (v9)
Pins reposicionados SOBRE la tierra del arte usando isPointInFill como sonda (Sinaloa
318,190 · Chiapas 548,350 · Yucatán 588,288 — antes caía en el mar). Cuadro de focus
eliminado en pins (outline:none + anillo blanco como indicador). Click en pin: otro pin =
viajar; pin activo = abrir detalle (dos clicks seguidos = viajar+detalle). Verificado 5/5 LAND.
Cierre de coherencia: videos, deep-links y contenido real (v13)
- 5 episodios con org REAL (datos extraídos de los minisitios originales): CDMX Marabunta, Chiapas Melel Xojobal, Jalisco Escuela Niñas y Niños Ciegos, Sinaloa Malala Academia, Yucatán Hunab Proyecto de Vida. Ya no hay “aliada por confirmar”.
- Videos YouTube por episodio: cdmx a4NLwWUXRF4 · chiapas -3N5QrKT_9Y (Nef pasó “Sinaloa” 2 veces; asumí el segundo = Chiapas) · jalisco _6fiX8hDL7M · sinaloa KtOu8p4JPCs · yucatán JAPysVUq8b4.
- Reglas ver-detalle: overview SIN botón (hint “doble click en el marcador”); dentro del detalle el panel muestra about real + ▶ Reproducir capítulo + link al minisitio original; el marcador lleva MINIATURA del video (i.ytimg.com) que también reproduce. Móvil: solo marcador (panel oculto en detalle).
- Reproductor modal vestido: youtube-nocookie embed, header con núm+título, X de marca, Esc/click-fuera cierran, iframe se vacía al cerrar.
- Deep-links capcdmx…#capyucatan (hash sync con replaceState; exitMap lo limpia).
- Acto 1: tarjeta “Involúcrate — Organizaciones” (5 links originales sv/*) y CTA “Material de apoyo ↗” → sv/apoyo.
- Bug cazado con instrumentación runtime: un
paintEpisode(0)de boot al final del archivo corría DESPUÉS de applyHash() y repintaba el episodio 0 (estado interno quedaba bien — active=3, hash correcto — solo el DOM se revertía). Fix:paintEpisode(active). - Verificado en cadena: deep-link → detalle → miniatura → player con video real cargando. 0 errores.
Hotfix v14: [hidden] vs display del autor
El reproductor salía al cargar y bloqueaba el scroll: .player{display:grid} (regla de autor)
le gana al [hidden]{display:none} del UA — misma familia de bug que mapact. Fix global:
[hidden]{display:none !important} al inicio del CSS. Verificado: arranque limpio, scroll ok,
flujo mapa→detalle→player→cerrar completo.