Motion · Eidos
Motion Eidos · sistema de dos momentos

Motion ocurre en dos momentos: la transición de estado (data-state, los presets de abajo) y la firma del evento (data-event-*, transitoria y cross-modal). Cada uno puede llevar animación — uno, el otro o ambos. Todo sale generado desde EidosConfig.motion. Abajo: los 5 presets CSS, la firma del evento, el driver spring (física real) y componentes reales animando por el sistema.

data-animation-style='fade'

fade

Opacidad pura. Bajo reduced-motion corre igual (es seguro).

fade
{`
`}
data-animation-style='scale-fade'

scale-fade

Escala (var(--motion-scale-enter)) + opacidad, con transform-origin. Bajo reduced-motion: solo fade.

scale-fade
{`
`}
data-animation-style='slide-fade' · data-side

slide-fade (side-aware)

Deslizamiento corto + opacidad según data-side (lo emite el motor de posicionamiento en un Popover real). Bajo reduced-motion: solo fade.

{#each SIDES as side}
{side}
{/each}
{`
`}
data-animation-style='slide-full' · data-side

slide-full (drawer)

Deslizamiento completo (100%) por borde — para drawers / sheets. Bajo reduced-motion: instantáneo.

{#each SIDES as side}
{side}
{/each}
{`
`}
data-animation-style='collapse'

collapse

Altura medida (--height, que pondría el componente). Bajo reduced-motion: instantáneo.

Contenido plegable.

La altura se anima entre 0 y la altura medida del contenido.

El componente (P2) mediría esto y pondría --height.

{`
`}
data-event-* · firma perceptiva (momento --event)

firma del evento (transitoria · cross-modal)

El otro momento: el flourish durante el hold de una señal de sema, genérico por family / intent / event (un commit asienta igual en todo el sistema). Aquí ves el canal motion; sema dispara además sonido + háptica desde el mismo evento. Bajo reduced-motion el motion se silencia, pero sonido/háptica siguen comunicando — esa es la ventaja cross-modal.

firma
{#each FIRMAS as f} {/each}
{`
`}
eidos.motion · driver 'spring'

spring (física real · driver JS)

Un muelle independiente por propiedad (no una cubic-bezier): overshoot y asentamiento que ninguna curva CSS expresa. Ajusta rigidez / amortiguación y reprodúcelo — el motor lo corre vía eidos.motion.enter/exit y lo trackea por elemento (cancelable). Hoy el demo es el consumidor directo; cablearlo a la Presence de los componentes es el siguiente paso.

spring
{`eidos.motion.register('pop', {
  driver: 'spring',
  enter: spring({ values: { opacity: [0, 1], scale: [0.4, 1] }, stiffness: ${stiffness}, damping: ${damping} })
})
eidos.motion.enter(el, 'pop', { dom: eidos.dom })`}
soma Presence · motion → spring

presence con spring (end-to-end)

El spring conduciendo un ciclo real de montaje/desmontaje. El panel usa una Presence de soma (el mismo primitivo que todo overlay) con motion = eidos.motion: al abrir entra con spring; al cerrar, Presence retiene el nodo hasta que el spring de salida termina, y entonces lo desmonta. El wrapper escribe data-animation-style; el engine de motion lee ese atributo y corre el preset JS. (En el preview en segundo plano el rAF está congelado; en un browser enfocado el panel rebota al entrar y se encoge al salir.)

{panelPresence.isPresent ? 'montado' : 'desmontado'}
{#if panelPresence.isPresent}
panel (spring presence)
{/if}
{`const presence = new Presence({ dom, open, ref, motion: eidos.motion })
// panel markup: 
→ motion.run runs the spring`}
eidos.motion · driver 'rect' (FLIP)

FLIP (transición de layout)

Transición de layout: el bloque cambia de tamaño y el driver rect mide el rect antes y después y anima el delta inverso (translate + scale) vía WAAPI — la técnica FLIP de Framer. El cambio de layout es instantáneo; la animación lo hace continuo. (Throttled en el preview en segundo plano.)

flip
{`const first = el.getBoundingClientRect()
mutateLayout()            // size / position changes
await tick()
eidos.motion.enter(el, 'flip', { dom, sourceRect: first })`}
eidos.motion.enter · presets JS expresivos

genie y cía (entradas dramáticas)

Más presets de driver JS al nivel de genie: warp tipo minimizar (genie, scale + translate + blur), giro 3D (flip-3d, rotateY), péndulo (swing) y muelle elástico con overshoot real (elastic). Pulsa Play y el motor corre cada entrada vía eidos.motion.enter; asientan en su sitio. (En el preview en segundo plano el rAF está congelado; enfócalo en un browser para verlas.)

{#each jsPresets as p (p.name)}
{p.label}
{/each}
{`eidos.motion.register('genie', {
  driver: 'waapi',
  enter: waapi([{ transform: 'translateY(70px) scale(0.18)', opacity: 0, filter: 'blur(6px)' },
                { transform: 'none', opacity: 1, filter: 'blur(0)' }],
               { duration: 650, easing: 'cubic-bezier(0.22, 1, 0.36, 1)' })
})
eidos.motion.enter(boxEl, 'genie')   // ▶ play`}
--motion-stagger-index × --motion-stagger-each · coreografía

stagger (Material 3 · list choreography)

Una lista entra en secuencia, sin orquestación JS: el contenedor pone el ritmo (--motion-stagger-each) y cada ítem su índice (--motion-stagger-index); el preset retrasa su entrada por índice × ritmo. El fill backwards mantiene cada ítem en su estado inicial hasta su turno. Compone con cualquier preset CSS — aquí slide-fade.

{#key staggerKey} {#each Array(6) as _, i (i)}
item {i + 1}
{/each} {/key}
{`
    {#each items as item, i}
  • …
  • {/each}
`}
data-animation-style · transiciones Material 3

shared-axis · fade-through (coreografía)

Presets de transición Material: shared-axis-x/y (movimiento direccional coordinado para cambios espaciales / navegacionales) y fade-through (para contenido sin relación — el entrante escala desde 0.92 + fade, el saliente solo opacidad). Bajo reduced-motion: solo fade. El container-transform ya está cubierto por el driver rect (FLIP).

{#each materialPresets as p (p)}
{p}
{/each}
{`
`}
[data-motion-set] · sets productive / expressive (F6)

productive ⇄ expressive (rigor de tokens · Carbon)

El mismo preset (scale-fade), dos personalidades. El set productive (default) usa el easing eficiente; expressive remapea las curvas a las emphasized de Carbon vía un scope [data-motion-set='expressive'] — sin tocar el componente. Las curvas son tokens: el theme retinta lo perceptualmente fijo, no inventa animaciones. Cambia el set y abre/cierra para sentir la diferencia.

{motionSet}
{`
`}
Componentes · vía el sistema de presets

Componentes reales

Estos overlays animan a través del sistema de presets de arriba — Dialog → scale-fade + fade, Popover → slide-fade, Drawer → slide-full. Sus animaciones bespoke se migraron al registro y cada uno conserva su timing exacto vía --motion-duration/ease por componente. La Presence de soma espera la animación del preset antes de desmontar. Ábrelos para verlo (tema + reduced-motion aplican, proyectados a documentElement).

Dialog · spring (JS · motion) Abrir dialog Confirmar

El contenido entra con un spring real (driver JS) — su Presence corre y espera el muelle vía motion. El overlay sigue con fade (CSS). Un overlay con nombre brincando.

Cerrar
Popover · slide-fade Abrir popover

Desliza desde el lado según data-side.

Cerrar
Drawer · slide-full Abrir drawer Panel

Entra deslizando completo desde el borde.

Cerrar
Toast · slide-fade