web-animations

Installation
SKILL.md

Web Animations (WAAPI + CSS Transitions + CSS Animations)

Guia de referencia para las 3 APIs nativas de animacion del browser. Complementa la skill view-transitions (navegacion entre estados/paginas — ver ../view-transitions/SKILL.md).

Cuando Usar Cada API

Necesidad API Por que
Hover, focus, estado simple CSS Transitions Declarativo, cero JS
Secuencia de keyframes pura CSS CSS Animations @keyframes + animation
Control runtime (pause, reverse, seek) WAAPI Element.animate() retorna Animation controlable
Scroll-driven reveal/parallax CSS animation-timeline o WAAPI ScrollTimeline Off main thread, sin jank
Entry desde display:none CSS Transitions + @starting-style + allow-discrete Nativo, sin JS
Animacion de height: auto CSS + interpolate-size Chrome 129+, progressive enhancement
Logica per-frame (fisica, canvas) requestAnimationFrame Ultimo recurso, main thread only

Web Animations API (WAAPI)

Element.animate()

const anim = element.animate(
  [{ transform: 'translateX(0)', opacity: 1 }, { transform: 'translateX(300px)', opacity: 0 }],
  {
    duration: 300,          // milisegundos (NO segundos como CSS)
    easing: 'ease-out',     // default es 'linear' (distinto a CSS!)
    iterations: 1,          // Infinity para loops
    fill: 'forwards',       // none | forwards | backwards | both
    direction: 'alternate', // normal | reverse | alternate | alternate-reverse
    delay: 100,
    composite: 'replace',   // replace | add | accumulate
  }
);

Keyframes: array de objetos (flexible, soporta offset/easing por keyframe) u objeto compacto ({ opacity: [0, 1] }). Implicit from/to: element.animate({ transform: 'translateX(300px)' }, 400) anima desde el valor actual. Siempre camelCase (backgroundColor).

El Objeto Animation

// Playback
anim.play();  anim.pause();  anim.reverse();  anim.finish();  anim.cancel();
// Estado
anim.playState;    // 'idle' | 'running' | 'paused' | 'finished'
anim.currentTime;  // ms | null
anim.playbackRate; // 1.0 default, negativo = reversa
anim.updatePlaybackRate(0.5); // smooth rate change
// Promises (async/await)
await anim.ready;    // animacion arranco
await anim.finished; // animacion termino
// Events: onfinish, oncancel, onremove

Persistir Estilos (Preferir sobre fill:'forwards')

anim.finished.then(() => { anim.commitStyles(); anim.cancel(); });

fill: 'forwards' mantiene memoria y tiene side-effects en la cascada. commitStyles() escribe los valores computados en element.style y libera recursos.

getAnimations()

element.getAnimations();  // del elemento (incluye CSS animations/transitions)
document.getAnimations(); // todas del documento

Para features avanzadas (KeyframeEffect constructor, compositing, pseudo-element targeting), leer references/waapi-advanced.md.

CSS Transitions

transition: opacity 200ms ease-out, transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1);

@starting-style — Entry Animations

Sin @starting-style, las transitions no se activan en el primer render (no hay estado "antes"):

.card {
  opacity: 1; transform: translateY(0);
  transition: opacity 300ms, transform 300ms;
  @starting-style { opacity: 0; transform: translateY(8px); }
}

Animar display con allow-discrete

.dialog {
  display: none; opacity: 0;
  transition: opacity 200ms ease, display 200ms allow-discrete;
}
.dialog[open] { display: block; opacity: 1; }
@starting-style { .dialog[open] { opacity: 0; } }

Eventos: transitionend, transitionrun, transitionstart, transitioncancel. Gotcha: transitionend NO se dispara si se interrumpe.

CSS Animations

@keyframes slide-in {
  from { transform: translateX(-100%); opacity: 0; }
  to   { transform: translateX(0);     opacity: 1; }
}

.element {
  animation: slide-in 300ms ease-out forwards;
  /* name | duration | easing | fill-mode */

  /* Composicion con otras animaciones */
  animation-composition: add; /* replace | add | accumulate */
}

Eventos

element.addEventListener('animationend', (e: AnimationEvent) => {
  e.animationName; // nombre del @keyframes
});
// Tambien: animationstart, animationcancel, animationiteration

Performance

Propiedades compositor (GPU, preferir siempre): transform, opacity, filter, clip-path Propiedades main thread (evitar animar): width, height, top, left, background-color, box-shadow

CSS Animations y WAAPI corren en compositor para las propiedades GPU. requestAnimationFrame siempre corre en main thread.

will-change: aplicar solo cuando la animacion es inminente (ej: .card:hover { will-change: transform; }). No aplicar a todo — cada capa promovida consume GPU memory.

Layout thrashing: no intercalar reads (offsetHeight) y writes (style.height) en un loop. Batch reads primero, luego writes.

Scroll-Driven Animations

Para scroll-driven animations (ScrollTimeline, ViewTimeline, animation-timeline, animation-range), leer references/scroll-driven.md.

Patrones Modernos

Para interpolate-size, prefers-reduced-motion, @supports guards y otros patrones modernos, leer references/patterns.md.

Gotchas

  1. WAAPI default easing es linear, no ease como CSS — siempre especificar
  2. WAAPI duration en milisegundos, CSS en segundos — duration: 300 = 300ms
  3. fill: 'forwards' tiene side-effects — preferir commitStyles() + cancel()
  4. Transitions no se activan sin estado previo — usar @starting-style o rAF delay
  5. transitionend no se dispara si se interrumpe — no depender como unico cleanup
  6. Compositor vs main thread — animar solo transform, opacity, filter, clip-path
  7. will-change no es gratis — cada capa consume GPU memory
  8. animation-duration: auto requerido para scroll-driven timelines en CSS

Browser Support (2025)

Feature Chrome Edge Firefox Safari
Element.animate() core 36+ 79+ 48+ 13.1+
commitStyles() / persist() 84+ 84+ 75+ 13.1+
@starting-style 117+ 117+ 129+ 17.5+
transition-behavior: allow-discrete 117+ 117+ 129+ 17.5+
Scroll-driven (CSS) 115+ 115+ flag 18+ (parcial)
ScrollTimeline / ViewTimeline JS 115+ 115+ flag 18+ (parcial)
interpolate-size 129+ 129+ No No

Checklist Pre-Deploy

[ ] prefers-reduced-motion implementado
[ ] Solo animar propiedades de compositor (transform, opacity, filter)
[ ] will-change aplicado selectivamente y removido post-animacion
[ ] Duraciones razonables (150-400ms transiciones, hasta 1s animaciones complejas)
[ ] @starting-style para entry animations
[ ] commitStyles() + cancel() en vez de fill:'forwards' persistente
[ ] Scroll-driven animations con @supports guard y fallback visible
[ ] Testeado en Chrome, Safari Y Firefox
Related skills

More from testacode/llm-toolkit

Installs
3
First Seen
Mar 30, 2026