Contrato de Arquitectura Frontend Pryne
Las decisiones estructurales fundamentales de los builds vanilla-JS de Pryne, por qué existen y los invariantes que no debes romper. Implementación de referencia: design-demos/ en el repositorio Portfolio (test) (~/Documents/Coding Folder/Portfolio (test)), build activo v4-d-hybrid.html + js/roll-*.js, a fecha de 2026-07-22.
Jerga, definida una vez: IIFE = Expresión de Función Invocada Inmediatamente, (function () { ... })(); — un cierre a nivel de archivo, para que nada se filtre excepto lo que el archivo coloca explícitamente en el espacio de nombres compartido. Mejora progresiva = la página es completamente utilizable sin mejoras JS; el comportamiento más enriquecido se superpone solo cuando su dependencia realmente se carga. Placa de desarrollo = la obra de arte del lienzo de este proyecto que se "desarrolla" (se resuelve a partir del ruido) a medida que el usuario interactúa.
Cuándo NO usar esta habilidad
- Teoría de animación, temporización, suavizado, coreografía → pryne-motion-reference
- Si un cambio está siquiera permitido y cómo se controla → pryne-change-control
- Algo está roto y estás diagnosticando → pryne-frontend-debugging
- Probar que un cambio funciona (capturas de pantalla, Lighthouse, rúbrica) → pryne-validation-and-qa
- Proyecto React/Next.js o cualquier proyecto con empaquetador: este contrato es solo para sitios vanilla sin compilación.
El contrato del módulo
Una preocupación por archivo. Una IIFE por archivo. Sin empaquetador, sin importaciones.
Todo el estado entre módulos reside en un único objeto de espacio de nombres global — window.Roll aquí. roll-core.js lo crea y se carga primero; todos los demás módulos abren con const Roll = window.Roll; y leen/escriben propiedades en él (roll-core.js:1-7 lo establece explícitamente). Los módulos que no deben conocerse entre sí se comunican a través de eventos del DOM (por ejemplo, roll-scroll.js envía roll:launch; roll-contact-sheet.js lo escucha — véase roll-scroll.js:35-37).
El orden de los scripts en el HTML es el grafo de dependencias. De v4-d-hybrid.html:429-437:
roll-core.js → roll-shader.js → roll-field.js → roll-audio.js → roll-cursor.js
→ vendor/lenis.min.js → roll-scroll.js → roll-contact-sheet.js → roll-eggs.js
Invariante: nunca reordenes o insertes un <script> sin primero buscar qué lee el módulo de Roll y confirmar que cada productor se carga antes. No hay un sistema de importación que te salve; un reordenamiento falla en silencio o lanza un error en la carga.
grep -n "Roll\.\w*" design-demos/js/<module>.js # qué lee/escribe
GSAP deliberadamente NO está en esa cadena — véase Invariante 2.
Mapa de módulos (a fecha de 2026-07-22)
| Archivo | Una preocupación | Líneas |
|---|---|---|
roll-core.js | Espacio de nombres, estado compartido, motor de placa de desarrollo, upgradeMotion | 192 |
roll-scroll.js | La columna vertebral: entrada, coreografía de desplazamiento, HUD, bucle rAF (requestAnimationFrame) principal, Lenis | 226 |
roll-field.js | Campo de fondo en canvas | 136 |
roll-shader.js | Sombreador WebGL de grano/luz + fallback a canvas-2D | 133 |
roll-eggs.js | Disparadores de personalidad ocultos (solo a nivel de susurro) | 83 |
roll-audio.js | Sonido ambiental suave vinculado a la velocidad de desplazamiento | 79 |
roll-contact-sheet.js | Revelación de la hoja de contactos al lanzamiento | 55 |
roll-cursor.js | Cursor personalizado (solo escritorio, controlado por GSAP) | 36 |
¿Nueva funcionalidad interactiva? Es un nuevo archivo roll-*.js (o una sección claramente delimitada del módulo que posee esa preocupación), añadido a la cadena de scripts en la posición de dependencia — no un <script> en línea en el HTML.
Invariantes (verificados en el código — mantenlos ciertos)
- El movimiento reducido controla cada módulo de movimiento.
Roll.reduced = matchMedia('(prefers-reduced-motion: reduce)').matches(roll-core.js:9). Cada módulo lo consulta: el sombreador salta WebGL (roll-shader.js:90,124), el campo salta su bucle rAF (roll-field.js:125,135), la hoja de contactos regresa temprano (roll-contact-sheet.js:12), el desplazamiento salta la entrada de mantener presionado para desarrollar y a Lenis (roll-scroll.js:39-40,152), y CSS deshace las transiciones de clip-path (v4-d-hybrid.html:293-295). Cualquier nuevo movimiento debe verificarRoll.reduced(JS) o estar bajo la consulta de medios de movimiento reducido (CSS). - GSAP es una mejora progresiva, nunca una dependencia. Se carga
asyncdesde CDN con un hash SRI (v4-d-hybrid.html:440); al cargarse, llama aRoll.upgradeMotion(), que cambiaRoll.enhancedy vacía las devoluciones de llamada registradas medianteRoll.onMotionReady(roll-core.js:10-15,186-191). Si GSAP nunca llega, el sitio debe seguir siendo completamente funcional — cursor, tipo de héroe y sesgo simplemente se quedan en la línea base (roll-cursor.js:34-35,roll-scroll.js:32-33,89,196). Registra el código dependiente de GSAP conRoll.onMotionReady(fn); nunca llames agsap.*de forma incondicional. - Las tomas de control visuales arriesgadas están envueltas defensivamente — en la ruta recurrente, no solo en la configuración. El cursor personalizado está protegido en tres capas, cada una con una captura restauradora que anula
Roll.cursorUpdatey elimina.custom-cursor(que oculta el cursor del SO): configuración (roll-cursor.js:9-32, captura en 32), el cuerpo del manejador por evento (roll-cursor.js:17-20) y el sitio de llamada depointermove(roll-core.js:26-29). Una falla en cualquier punto — en la configuración o en cualquier mousemove posterior — restaura el cursor del SO (corregido el 2026-07-22; contrato a prueba de fallos en el encabezado del archivo,roll-cursor.js:1-5). Al aplicar este patrón a cualquier cosa que oculte/reemplace una capacidad nativa, protege también la ruta recurrente, no solo la configuración. - Las revelaciones de clip-path viven en un envoltorio interno, nunca en el elemento que IntersectionObserver observa (la "corrección concepto-C" — llamada así por el concepto v4 C,
v4-c-ch001.html, donde se originó antes de ser llevada a v4-d). El IO observa.panel; el clip-path anima.panel-inner(v4-d-hybrid.html:121-122,185-186). Justificación: recortar el elemento observado cambia su geometría de intersección y la revelación lucha contra el activador. - Los elementos visuales impulsados por la interacción se controlan por su propia visibilidad, no por la carga de la página. Las placas de características solo se desarrollan mientras su panel tiene
.in(roll-scroll.js:204-219); el comentario del código registra el error original: las placas terminaban de resolverse antes de que alguien se desplazara hacia ellas, "derrotando silenciosamente la interacción de 'se desarrolla mientras interactúas'". El comentario principal dev4-d-hybrid.htmlllama a esta corrección la de mayor prioridad de la auditoría técnica. - WebGL es explícito y seguro con fallback. Alfa no premultiplicado en la creación del contexto (
roll-shader.js:17),gl.clearexplícito en cada fotograma (roll-shader.js:95), el contexto 2D se adquiere solo después de que WebGL haya tenido su oportunidad — un canvas solo puede tener un tipo de contexto (roll-shader.js:9-12), ywebglcontextlostfalla silenciosamente en lugar de permitir que el bucle de renderizado lance una excepción (roll-shader.js:129-132). Los llamadores usan el único punto de entradaRoll.shader.render(t)y nunca saben si es GL o 2D. - El desplazamiento sigue siendo nativo bajo Lenis. Lenis está incluido como proveedor (
js/vendor/lenis.min.js, no CDN), instanciado solo cuando!Roll.reduced, y mantienewindow.scrollYválido — es una mejora de sensación, no un secuestro de desplazamiento (roll-scroll.js:148-152,167). El bucle de renderizado leescrollYcomo antes.
Filosofía de demo de un solo archivo + tokens de diseño
Cada variante de diseño es un archivo HTML autocontenido (CSS en línea, módulos js/ compartidos) para que las variantes se puedan comparar, capturar en pantalla y probar A/B como artefactos completos — véase pryne-design-direction-decision. Un comentario largo al inicio de cada archivo registra qué es la variante y qué hallazgos de auditoría lleva (léelo antes de editar; es el registro de cambios local del archivo).
Todos los colores, espaciados y suavizados son propiedades personalizadas CSS en :root (v4-d-hybrid.html:50-63):
:root {
--bg: #0a0a0b; --accent: #d2232e; --ink: #f4f3f1; --dim: #86868c;
--pad-x: clamp(1.25rem, 6vw, 6rem);
--ease-out: cubic-bezier(0.22, 1, 0.36, 1);
--ease-snap: cubic-bezier(0.16, 1, 0.3, 1);
}
Según las reglas globales de Pryne: nunca codifiques un color/tamaño donde exista un token; los valores fluidos usan clamp(). El nuevo movimiento debe reutilizar --ease-out/--ease-snap a menos que pryne-motion-reference diga lo contrario — un lenguaje de suavizado por build.
Lista de verificación: añadir un módulo
- Nuevo
roll-*.js(o módulo existente correcto propietario), IIFE de nivel superior,const Roll = window.Roll; -
<script>insertado después de todo lo que lee deRoll, antes de cualquier cosa que lo lea - Verifica
Roll.reduced; el uso de GSAP solo dentro deRoll.onMotionReady - Cualquier cosa que oculte una capacidad nativa está envuelta en try/catch con una captura restauradora
- Controlado por visibilidad si está impulsado por interacción; objetivo de IO ≠ objetivo de clip-path
- Tokens, no valores codificados
- Luego valida según pryne-validation-and-qa
Puntos débiles conocidos (abiertos, expresados claramente)
- Sin paso de compilación: sin minificación, sin empaquetado; bien para demos, sin resolver para despliegue en producción.
- Las fuentes se cargan desde CDN de Google Fonts (
v4-d-hybrid.html:47-48) a pesar de la intención inicial del proyecto de usar proveedores propios (el autoalojamiento se menciona endesign-demos/DECISIONS.mdsolo de pasada, dentro de la entrada de Lenis; el registro de decisiones en sí mismo es anterior al archivo; aún no se ha hecho). GSAP también sigue en CDN (fijo + SRI). Consulta pryne-change-control antes de "arreglar" cualquiera de ellos. - Sin pruebas automatizadas — la validación es solo evidencia basada en navegador (pryne-validation-and-qa).
- Contenido de marcador de posición: los estudios de caso/biografía no son definitivos; no pulas el texto como si lo fuera.
Procedencia y mantenimiento
Ejecuta scripts/verify.sh (en el directorio de esta habilidad) para verificar todo lo siguiente de una vez.
Fundamentado en el repositorio Portfolio (test) en el commit 457868a, 2026-07-22. Vuelve a verificar antes de confiar en hechos volátiles:
- Orden de scripts:
grep -n "<script" design-demos/v4-d-hybrid.html - Recuento de líneas / lista de módulos:
wc -l design-demos/js/*.js - Cobertura de movimiento reducido:
grep -rn "Roll.reduced\|prefers-reduced-motion" design-demos/ - GSAP sigue siendo CDN+SRI, fuentes siguen siendo CDN:
grep -n "cdn.jsdelivr\|fonts.googleapis" design-demos/v4-d-hybrid.html - Lenis sigue siendo proveedor:
ls design-demos/js/vendor/ - El build activo sigue siendo v4-d: verifica
design-demos/DECISIONS.mdy el últimogit log --oneline