Vuelta al Cóndor 2025
November 24, 2025 · View on GitHub
Sitio estático oficial de la Vuelta al Cóndor 2025, un desafío de ciclismo de ruta autosuficiente de 187 km y 3000 m+ organizado por El Rey de la Montaña. El proyecto prioriza la accesibilidad (WCAG 2.2 AA), el rendimiento (Core Web Vitals ≥ 95) y la comunicación inclusiva para toda la comunidad LGBTQI+.
Estructura del proyecto
/
├─ index.html # Landing principal con hero, ruta, altimetría, FAQ y reglamento
├─ corredores.html # Página informativa “Próximamente” para el listado de riders
├─ styles.css # Hoja de estilos editable (tokens --vac-*)
├─ styles.min.css # Versión minificada para producción
├─ sw.js # Service Worker (cache estática + dinámica)
├─ site.webmanifest # Manifest PWA (maskable icons, theme, start_url)
├─ data/sponsors.json # Logos, enlaces y CTA de patrocinadores
├─ route/vac.gpx # Track oficial validado (GPX)
└─ images/ # Identidad visual, altimetría y recursos multimedia
Requisitos rápidos
- Node.js ≥ 20.19.4 (recomendado). El proyecto incluye
.nvmrc; usanvm use(macOS/Linux) onvm use 20.19.4(Windows) para fijar versión. - Lighthouse (CLI o Chrome DevTools) para validar CWV.
Entorno Node
- Versionado local:
.nvmrcespecifica20.19.4. - macOS/Linux:
nvm instally luegonvm use. - Windows (nvm‑windows):
nvm install 20.19.4ynvm use 20.19.4.
Desarrollo local
# Minificar estilos (mantén styles.css como fuente de verdad)
npx clean-css-cli styles.css -o styles.min.css
Desarrollo con CSP (sin tocar HTML / .htaccess)
- Servidor de desarrollo con Node/Express que aplica una CSP relajada por cabecera y remueve la meta CSP del HTML al vuelo.
- No modifica archivos fuente; solo aplica en localhost.
# Arrancar servidor de desarrollo
npm run dev # node dev-server.js
# Arrancar con recarga automática (watch)
npm run dev:auto # nodemon observa .html, .css, .js, .json
# Acceso
http://localhost:5174/
- Qué hace:
- Cabecera CSP en dev: permite
http:,https:,data:,blob:, inline y eval; no bloquea recursos reescritos por extensiones locales. - Elimina la meta CSP de las respuestas HTML para evitar combinación de políticas.
- Cabecera CSP en dev: permite
- Producción: usa la CSP estricta definida en HTML, con orígenes limitados (Google Fonts/GA) y
'self'.
Modo estricto local (simular producción)
- Para validar con la misma CSP estricta que en producción, arranca el servidor con
STRICT=1:- PowerShell:
$env:STRICT = 1; pnpm run dev - CMD (Windows):
cmd /c "set STRICT=1 && pnpm run dev"
- PowerShell:
- En este modo, el servidor devuelve por cabecera la política estricta (sin
'unsafe-inline'ni'unsafe-eval') y sigue eliminando la meta CSP del HTML para evitar conflictos.
Requisitos de CSP por entorno
- Desarrollo (relajado): permite
'unsafe-inline'y'unsafe-eval'para evitar falsos positivos causados por extensiones locales y tooling. La meta CSP se elimina al vuelo. - Staging (GitHub Pages): no hay cabecera CSP del servidor. La política se define por meta CSP dentro de
index.html,guia.htmlycorredores.html. - Producción: política estricta en HTML (meta CSP) o por cabecera del servidor si decides moverla a
.htaccess. Asegura paridad con las directivas del HTML.
Confirmación: no se requiere unsafe-eval
- Se auditó el código del sitio (scripts y bibliotecas incluidas) y no usa
eval,new FunctionnisetTimeout/Intervalcon cadenas. - Bibliotecas WebGL (
gpu-io.min.js) generan y compilan shaders, pero no evalúan código JavaScript dinámico. - Por lo tanto,
'unsafe-eval'permanece excluido en todos los entornos. Si una biblioteca futura lo exigiera, debe reemplazarse o refactorizarse.
Notas de validación
- GA4 (gtag.js) puede registrar
net::ERR_ABORTEDen desarrollo/strict por privacidad/red; estos errores no afectan la funcionalidad. - Ejecuta
pnpm run lhcipara confirmar SEO/Best Practices una vez aplicada la CSP estricta. - Para comprobar la política rápidamente:
pnpm run check:csp(usatools/ci/check_csp.js; preferirá cabecera en.htaccessy, si no existe, verificará la meta CSP deindex.html).
Recomendaciones
- No desplegar el server de desarrollo; es solo para localhost.
- Validar en producción sin extensiones que reescriban recursos.
- Si necesitas probar CSP estricta en dev, arranca sin
dev:autoy ajusta temporalmenteDEV_CSPendev-server.js.
Accesibilidad y QA
- Teclado: recorre la interfaz completa, verifica
skip-link, menú móvil y foco visible. - Lectores de pantalla: comprueba estructura semántica (NVDA / VoiceOver) y textos alternativos.
- Contraste: usa herramientas como Axe, Stark o
npx @axe-core/cli http://localhost:8000. - Preferencias de usuario: valida
prefers-reduced-motion, contraste alto y navegación offline (PWA instalada). - Lighthouse: ejecuta
npx lighthouse http://localhost:8000 --viewy guarda el reporte con puntuaciones ≥ 95.
Privacidad y Analítica
- Analítica: se usa Google Analytics 4 (GA4) con carga diferida.
- Respeto a Do Not Track (DNT): si el usuario tiene DNT activo, no se carga
gtag.jsni se envían eventos. - CSP: las páginas incluyen Content Security Policy restringiendo orígenes (
googletagmanager.comygoogle-analytics.compara scripts/conexiones de GA). - PII: el sitio no recolecta ni transmite datos personales identificables; sólo métricas agregadas de uso cuando DNT está desactivado.
Decisiones técnicas
- Rendimiento de red:
preconnectydns-prefetchagoogletagmanager.comygoogle-analytics.comen todas las páginas. - Carga de imágenes:
loading="lazy"ydecoding="async"en imágenes no críticas; logotipo confetchpriority="high"por estar above‑the‑fold. - Service Worker: caché estática de páginas clave (
/,/guia.html) y activos (styles.min.css,gpu-io.min.js,fluid-background.js, SVG de ruta y altimetría); estrategiascacheFirst,staleWhileRevalidateynetworkFirstsegún tipo. - Recarga controlada: el
sw.jsnotifica versión nueva (vac-update) y las páginas realizan una recarga única para aplicar cambios. - Analítica: GA4 cargado de forma diferida y condicionado por DNT.
- Animaciones: botón de “modo rendimiento” que reduce animaciones GPU y oculta fireworks.
- Navegación: índice de guía sticky y colapsable en móvil con scroll‑spy; barra de accesos rápidos y botón “↑ Índice”.
Diseño responsive
- Breakpoint principal
900px: reflujo a una columna, sticky para quickbar y toc; grids (.guide-grid-2,#tool .grid-2/3) pasan a 1 columna. - Tipografía y espaciamiento fluidos, respetando tokens
--vac-*enstyles.css/styles.min.css. - Navegación móvil accesible: foco atrapado,
aria-expanded,Escapepara cerrar yskip-linkactivo.
Validación multiplataforma
- Desktop: Chrome, Edge, Firefox (Windows/macOS) — verificación visual + Lighthouse.
- Móvil: Chrome Android, Safari iOS — comprobar sticky/colapsable, quickbar, rendimiento con modo bajo.
- PWA: instalación, navegación offline (caché estática) y actualización de caché al publicar.
Pruebas de usabilidad
- Acceso directo a guía desde home (CTA y promo) y dentro de guía (índice + quickbar).
- Flujo de herramienta: inputs claros, validación mínima y exportación a
.txte impresión.
Rendimiento
- Scripts no críticos con
defer, restauración deconsoletras carga. - Imágenes grandes en SVG con lazy y tamaños definidos; preloads mínimos del hero.
- Recursos de terceros (GA) cargados asíncronamente; errores locales esperados en desarrollo.
Actualizaciones y Service Worker
- Versionado en
sw.js:APP_VERSION: etiqueta de versión (ej.YYYY-MM-DD) usada para notificar a clientes.STATIC_CACHE/DYNAMIC_CACHE: nombres de caché. Cambia los sufijos cuando hay cambios mayores en la política de caché.
- Señal de actualización: al activar, el SW envía
{ type: 'vac-update', version: APP_VERSION }a todas las pestañas controladas. - Recarga controlada en páginas:
- Las páginas escuchan el mensaje
vac-updatey, si la versión difiere, guardanvac_versiony recargan una sola vez (con llave por ruta ensessionStoragepara evitar loops). - Se puede forzar verificación inmediata enviando
postMessage({ type: 'VAC_CHECK_VERSION' })desde la página al controlar el SW.
- Las páginas escuchan el mensaje
- Estrategias:
- Documentos y CSS/JS críticos:
cacheFirstpara rapidez (con actualización en background). - Imágenes y datos (
/data/):staleWhileRevalidate. - Resto:
networkFirstcon fallback offline.
- Documentos y CSS/JS críticos:
Caché y Headers (.htaccess)
sw.jssin caché fuerte para propagar cambios inmediatamente:
<IfModule mod_headers.c>
<Files "sw.js">
Header set Cache-Control "no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
Header set Expires "0"
</Files>
</IfModule>
- HTML sin caché fuerte en producción para asegurar contenido fresco (opcional si el SW controla):
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 0 seconds"
# Activos versionados (CSS/JS/img) pueden tener caché largo
ExpiresByType text/css "access plus 1 year"
ExpiresByType application/javascript "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
</IfModule>
- Sugerencias de seguridad (opcionales):
X-Content-Type-Options: nosniffReferrer-Policy: strict-origin-when-cross-originX-Frame-Options: SAMEORIGINPermissions-Policy: interest-cohort()
Despliegue
- Netlify / Vercel: arrastra la carpeta del proyecto o conecta el repositorio (build vacío, output
/). - GitHub Pages: publica desde rama principal o carpeta
docs/con contenido idéntico. - Servidor tradicional / CDN: copia los archivos tal cual; invalida cachés al actualizar
styles.min.csso imágenes pesadas.
Staging (GitHub Pages)
- URL de staging:
https://cripterhack.github.io/Vuelta-al-Condor/. - Compatibilidad de rutas: se ajustaron todas las referencias a recursos a rutas relativas (
styles.min.css,images/...,route/vac.gpx,site.webmanifest) para funcionar bajo subpath de Pages. - CD automático: se incluye
/.github/workflows/pages.ymlque construye estilos (autoprefix + minify), genera variantes de imagen y despliega a Pages en cada push amain/master. - Activación: en la configuración del repositorio (“Pages”), el workflow
Deploy to GitHub Pageshabilita el entornogithub-pagesautomáticamente; no requiere secretos. - Fallback noscript: en
index.htmlse aplica la técnicapreload as=stylecononloady un<noscript>de respaldo, combinada con CSS crítico inline para mejorar LCP.
Nota de CSP y despliegue
- El hosting no debe añadir cabeceras CSP que contradigan la meta CSP del HTML, salvo que quieras mover la CSP al servidor. Si lo haces, asegúrate de mantener las mismas directivas.
Flujo de publicación
- Bump de versión:
- Actualiza
APP_VERSIONensw.js. - Si cambias la política de caché, actualiza sufijos de
STATIC_CACHEyDYNAMIC_CACHE. - Versiona recursos estáticos con query (
styles.min.css?v=YYYYMMDDHHMM) o nombres con hash.
- Actualiza
- Despliegue:
- Publica los archivos; el navegador recibirá el SW nuevo.
- Páginas recibirán
vac-updatey se recargarán una sola vez para aplicar contenido.
- Validación:
- DevTools → Application → Service Workers: pulsa “Update” y observa la recarga única.
- Network: verifica que
sw.jsno se cachea fuerte y que HTML responde sinExpireslargo.
Checklist de migración
- Tokens
--vac-*aplicados en estilos y componentes. - JSON-LD (SportsEvent, WebSite, Breadcrumb) con datos actuales de VAC.
- Manifest + Service Worker operativos (experiencia offline funcional).
-
APP_VERSIONactualizado y recarga controlada probada (sin loops). -
sw.jscon headers de no‑cache en.htaccess. - DNT activo: GA4 no carga; DNT desactivado: GA4 carga diferido.
- Track
route/vac.gpxdescargable y sincronizado. - Secciones hero, detalles, ruta, altimetría, patrocinadores, FAQ, reglamento y contacto alineadas con el brief.
- Página
corredores.htmlvisible en navegación con estado “Próximamente”. - Resultados Lighthouse ≥ 95 en Performance, Accessibility, Best Practices y SEO.
- Verificado desarrollo en localhost con
npm run dev:autosin errores CSP. - Despliegue sin cabeceras CSP adicionales que contradigan la meta CSP.
Créditos y derechos
- Identidad visual y recursos de marca: propiedad de sus titulares; uso autorizado para este sitio.
- Logos de patrocinadores y fotografías: publicados con permiso; revisar antes de reutilizar en otros contextos.
- Desarrollo: IzignaMx. Contribuciones y mejoras son bienvenidas mediante PR.
Desarrollado por IzignaMx