---
name: web-animation-pro
description: Animação web profissional pra landing pages — CSS animations/transitions modernas, GSAP timelines, scroll-driven animations (View Timeline API), Framer Motion (React), Lottie web, micro-interactions. Otimizado pra conversão sem perder Core Web Vitals.
owner: "@web-designer"
shared_with: ["@web-designer", "@brand-director"]
version: 1.0
tags: [animation, css, gsap, framer-motion, scroll, lottie, web, landing, conversion]
---

# Skill: Web Animation Pro

## Quando usar

Sempre que @web-designer produzir landing/page interativa que precisa de:
- Hero animado (entrada, parallax, gradient breathing)
- Scroll-driven animations (text reveal, image pin, progress bar)
- Micro-interactions (hover, click, form feedback)
- Animação SVG/Lottie (logo, ícone, ilustração)
- Counter animados (social proof)
- Transições entre seções (smooth scroll, fade between)
- Loading states / skeleton screens

## Stack (ordem de preferência)

| Engine | Quando usar | Pro | Contra |
|---|---|---|---|
| **CSS** (transitions + @keyframes + view-timeline) | 80% dos casos | Native, performance, sem JS bundle | Limitado pra física complexa |
| **GSAP** | Timelines complexas, sequenciamento, ScrollTrigger | API rica, 60fps garantido | +35KB gzip |
| **Framer Motion** | React/Next.js, declarativo | Type-safe, spring physics | +18KB gzip, só React |
| **Motion One** | Web Animations API + spring | 3.8KB tiny, native API | Menos features que GSAP |
| **Lottie Web** | Animação vetorial complexa (AE export) | Resolução infinita, leve | Precisa AE/Figma origin |
| **View Timeline API** (CSS scroll-driven) | Scroll progress animations | Zero JS, native | Suporte browsers 2024+ |
| **Anime.js** | Alternativa GSAP mais leve | 17KB, simple API | Menos features ScrollTrigger |

## Regra de ouro

**Comece em CSS. Suba pra GSAP só se CSS não cobrir.** Cada engine extra é peso e complexidade.

## Receitas (decoreba operacional)

### 1. Hero fade-in + slide-up staggered (CSS puro)

```css
.hero-text {
  opacity: 0;
  transform: translateY(40px);
  animation: heroEntry 600ms cubic-bezier(.4,0,.2,1) forwards;
  animation-delay: var(--delay, 0ms);
}
.hero-text.subhead { --delay: 150ms; }
.hero-text.cta     { --delay: 300ms; }
@keyframes heroEntry {
  to { opacity: 1; transform: translateY(0); }
}
```

### 2. Scroll-driven progress bar (View Timeline API — native CSS, sem JS)

```css
.progress-bar {
  position: fixed; top: 0; left: 0; height: 4px;
  background: var(--brand-primary);
  width: 100%; transform-origin: left;
  animation: scaleProgress linear;
  animation-timeline: scroll(root block);
}
@keyframes scaleProgress {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
```

### 3. Section reveal on scroll (View Timeline)

```css
.fade-in-on-scroll {
  opacity: 0;
  transform: translateY(60px);
  animation: revealIn linear both;
  animation-timeline: view();
  animation-range: entry 0% cover 30%;
}
@keyframes revealIn {
  to { opacity: 1; transform: translateY(0); }
}
```

### 4. Smooth scroll + scroll snap

```css
html { scroll-behavior: smooth; scroll-snap-type: y mandatory; }
section { scroll-snap-align: start; min-height: 100vh; }
```

### 5. Hover micro-interaction (botão CTA premium)

```css
.cta {
  position: relative; overflow: hidden;
  transition: transform .2s cubic-bezier(.4,0,.2,1);
}
.cta::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(120deg, transparent, rgba(255,255,255,.3), transparent);
  transform: translateX(-100%);
  transition: transform .6s cubic-bezier(.4,0,.2,1);
}
.cta:hover { transform: scale(1.03); }
.cta:hover::after { transform: translateX(100%); }
```

### 6. Counter animado social proof (Intersection Observer + requestAnimationFrame)

```html
<span class="counter" data-target="10000" data-duration="1500">0</span>
```

```js
const animate = (el) => {
  const target = +el.dataset.target;
  const duration = +el.dataset.duration || 1500;
  const start = performance.now();
  const step = (now) => {
    const t = Math.min((now - start) / duration, 1);
    const eased = 1 - Math.pow(1 - t, 3); // ease-out cubic
    el.textContent = Math.floor(target * eased).toLocaleString('pt-BR');
    if (t < 1) requestAnimationFrame(step);
  };
  requestAnimationFrame(step);
};
const io = new IntersectionObserver((entries) => {
  entries.forEach(e => {
    if (e.isIntersecting) { animate(e.target); io.unobserve(e.target); }
  });
}, { threshold: 0.5 });
document.querySelectorAll('.counter').forEach(el => io.observe(el));
```

### 7. GSAP timeline complexa (hero com 5+ elementos sequenciados)

```js
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);

const tl = gsap.timeline({ defaults: { ease: 'power3.out', duration: 0.6 } });
tl.from('.hero-bg',     { opacity: 0, duration: 1.2 })
  .from('.hero-badge',  { y: -20, opacity: 0 }, '-=0.6')
  .from('.hero-h1',     { y: 60, opacity: 0 }, '-=0.3')
  .from('.hero-sub',    { y: 30, opacity: 0 }, '-=0.4')
  .from('.hero-cta',    { scale: 0, ease: 'back.out(1.7)' }, '-=0.2')
  .from('.hero-proof',  { opacity: 0 }, '-=0.2');
```

### 8. GSAP ScrollTrigger — pin + parallax + reveal

```js
gsap.to('.parallax-bg', {
  yPercent: -30,
  ease: 'none',
  scrollTrigger: {
    trigger: '.parallax-section',
    start: 'top bottom',
    end: 'bottom top',
    scrub: true,
  },
});

// Pin section + scrub through cards
gsap.to('.cards', {
  xPercent: -75,
  scrollTrigger: {
    trigger: '.horizontal-section',
    pin: true,
    scrub: 1,
    end: '+=2000',
  },
});

// Section reveal stagger
gsap.from('.feature-card', {
  y: 80, opacity: 0, stagger: 0.15, duration: 0.8,
  scrollTrigger: { trigger: '.features', start: 'top 70%' },
});
```

### 9. Framer Motion (React/Next.js)

```tsx
import { motion } from 'framer-motion';

<motion.div
  initial={{ opacity: 0, y: 40 }}
  animate={{ opacity: 1, y: 0 }}
  transition={{ duration: 0.6, ease: [0.4, 0, 0.2, 1] }}
>
  <h1>Headline</h1>
</motion.div>

// Stagger automático
<motion.ul
  initial="hidden"
  whileInView="visible"
  viewport={{ once: true, amount: 0.3 }}
  variants={{ visible: { transition: { staggerChildren: 0.1 } } }}
>
  {items.map((item, i) => (
    <motion.li
      key={i}
      variants={{
        hidden:  { opacity: 0, y: 20 },
        visible: { opacity: 1, y: 0 },
      }}
    >
      {item}
    </motion.li>
  ))}
</motion.ul>

// Spring physics
<motion.div
  whileHover={{ scale: 1.05 }}
  whileTap={{ scale: 0.95 }}
  transition={{ type: 'spring', stiffness: 300, damping: 20 }}
/>
```

### 10. Lottie web (animação vetorial AE)

```html
<script src="https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js"></script>
<lottie-player
  src="https://assets.lottiefiles.com/packages/lf20_xxx.json"
  background="transparent"
  speed="1"
  style="width: 300px; height: 300px"
  loop
  autoplay
></lottie-player>
```

```js
// Trigger on scroll
import lottie from 'lottie-web';
const anim = lottie.loadAnimation({
  container: document.querySelector('.lottie-container'),
  renderer: 'svg',
  loop: false,
  autoplay: false,
  path: '/assets/success.json',
});
new IntersectionObserver(([e]) => {
  if (e.isIntersecting) anim.play();
}, { threshold: 0.5 }).observe(anim.wrapper);
```

### 11. SVG morph (SMIL ou GSAP)

```html
<svg viewBox="0 0 100 100">
  <path d="M50,10 L90,90 L10,90 Z" fill="var(--brand)">
    <animate attributeName="d"
             values="M50,10 L90,90 L10,90 Z;
                     M10,10 L90,10 L90,90 L10,90 Z;
                     M50,10 L90,90 L10,90 Z"
             dur="3s" repeatCount="indefinite"/>
  </path>
</svg>
```

### 12. Skeleton loader (loading state)

```css
.skeleton {
  background: linear-gradient(90deg, #f0f0f0 0%, #e0e0e0 50%, #f0f0f0 100%);
  background-size: 200% 100%;
  animation: skeletonPulse 1.5s ease-in-out infinite;
}
@keyframes skeletonPulse {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
```

### 13. Pulse infinito no CTA (urgência sutil)

```css
.cta-urgent {
  animation: pulseUrgent 2s ease-in-out infinite;
}
@keyframes pulseUrgent {
  0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255,0,0,.5); }
  50%      { transform: scale(1.02); box-shadow: 0 0 0 12px rgba(255,0,0,0); }
}
```

### 14. Gradient breathing (background vivo)

```css
.bg-hero {
  background: linear-gradient(135deg, #1a1a1a, #2d2d2d, #1a1a1a);
  background-size: 200% 200%;
  animation: gradientShift 8s ease-in-out infinite;
}
@keyframes gradientShift {
  0%, 100% { background-position: 0% 0%; }
  50%      { background-position: 100% 100%; }
}
```

### 15. Form field feedback (success/error)

```css
.input { transition: border-color .2s, transform .2s; }
.input.is-valid {
  border-color: #10b981;
  animation: successPop .3s cubic-bezier(.68,-.55,.27,1.55);
}
.input.is-error {
  border-color: #ef4444;
  animation: shake .3s cubic-bezier(.36,.07,.19,.97);
}
@keyframes successPop {
  0% { transform: scale(1); }
  50% { transform: scale(1.02); }
  100% { transform: scale(1); }
}
@keyframes shake {
  0%, 100% { transform: translateX(0); }
  25%      { transform: translateX(-6px); }
  75%      { transform: translateX(6px); }
}
```

## Princípios (12 leis do motion aplicadas a web)

(Detalhes na skill compartilhada `motion-design-system` do @web-designer)

1. **Hook em 3 scrolls** — primeira dobra precisa engajar em <3s
2. **Performance > beleza** — 60fps sempre. Se anim trava, simplificar
3. **Respeitar `prefers-reduced-motion`** — sempre fallback (acessibilidade)
4. **Easing custom** — `cubic-bezier(.4,0,.2,1)` é o material design padrão
5. **Stagger 100-300ms** — entre elementos da mesma família
6. **Durations curtas** — UI: 150-300ms / Hero: 600-1200ms / Scroll: scrubbed
7. **Will-change parcimônia** — só em elementos animados, remover depois
8. **Transform/opacity only** — animar `width`/`height`/`top` quebra performance
9. **Anchor scroll-driven** — scroll é a melhor timeline (View Timeline API)
10. **Mobile-first** — testar em throttled CPU 4x slowdown
11. **Cubic-bezier(.68,-.55,.27,1.55)** — bounce premium pra CTAs/destaques
12. **Easing in vs out** — entrada `ease-out`, saída `ease-in`, ambos `ease-in-out`

## Acessibilidade (REGRA INEGOCIÁVEL)

```css
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
```

JS:
```js
const reduceMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduceMotion) {
  // animar
}
```

## Performance (Core Web Vitals impact)

| Métrica | Impacto animação |
|---|---|
| **LCP** (Largest Contentful Paint) | Hero animação não pode atrasar o LCP element. Animar APÓS LCP fired |
| **CLS** (Cumulative Layout Shift) | NUNCA animar `width`/`height` que causem shift. Use `transform` |
| **INP** (Interaction to Next Paint) | Animação no hover/click <100ms |
| **FCP** (First Contentful Paint) | CSS animation > JS animation (parse mais rápido) |

### Otimização

- **`transform` e `opacity`** = GPU accelerated (compositor thread)
- **`will-change`** = hint, mas remover após anim (memória)
- **`contain: layout style paint`** = isolar repaint
- **`content-visibility: auto`** = lazy render off-screen
- **Lottie SVG renderer** pra <50 layers, **Canvas** acima disso
- **GSAP `force3D: true`** força GPU em transform

## Bundle budget

| Engine | Peso gzip | Quando vale |
|---|---|---|
| CSS only | 0KB | Sempre primeiro recurso |
| Motion One | 3.8KB | Spring native + leve |
| Anime.js | 17KB | Timelines simples sem ScrollTrigger |
| GSAP core | 23KB | Timelines complexas |
| GSAP + ScrollTrigger | 35KB | Scroll-driven complexo |
| Framer Motion | 18KB | React, vale quando já tem |
| Lottie Web | 60KB | Vale só se vetor complexo |

**Budget recomendado:** ≤50KB total JS de animação numa landing.

## Stack por tipo de projeto

| Projeto | Stack |
|---|---|
| Landing simples (1 page) | CSS only + Intersection Observer |
| Landing premium (hero animado complexo) | CSS + GSAP ScrollTrigger |
| SaaS interativo (React) | Framer Motion + CSS |
| Portal/dashboard | CSS + Motion One (leve) |
| Webflow/Cloudflare Pages vanilla | CSS + GSAP CDN |
| Storytelling longform | GSAP + scroll-driven CSS |
| Email design | CSS only (max compatibilidade) |
| Ad criativo HTML5 | CSS + small JS |

## Decision tree (@web-designer)

```
Briefing chegou com necessidade de animação:
  ├─ É hover/click micro-interaction?       → CSS transition (sempre)
  ├─ É fade-in/slide-up entrada?            → CSS animation + IntersectionObserver
  ├─ É scroll progress simples?             → View Timeline API (CSS only)
  ├─ É scroll com pin/parallax/horizontal?  → GSAP ScrollTrigger
  ├─ É vetor complexo (AE/Figma origin)?    → Lottie
  ├─ É contagem/counter?                    → requestAnimationFrame custom
  ├─ É loading/skeleton state?              → CSS only
  ├─ É form feedback (success/error)?       → CSS animations
  ├─ É hero com timeline complexa?          → GSAP timeline
  └─ É vídeo programático embedado?         → Remotion (@web-designer) OR HyperFrames
```

## Integração com @web-designer (vídeo)

@web-designer usa **CSS/GSAP/Lottie** em landings.
@web-designer usa **Remotion/HyperFrames** pra renderizar MP4/WebM que @web-designer embeda na landing como `<video>` ou Lottie embed.

**Compartilhamento:** quando @web-designer precisa entender lógica de animação programática (Remotion props, HyperFrames blocks), lê as skills owned do @web-designer (`remotion-advanced`, `hyperframes-integration`, `lottie-integration`, `motion-design-system`).

## Validações pré-deploy

- [ ] `prefers-reduced-motion` respeitado
- [ ] 60fps em mobile throttled (DevTools Performance)
- [ ] Animação não atrasa LCP (fire APÓS LCP element render)
- [ ] Zero CLS por animação (não animar dimensões que causem reflow)
- [ ] Bundle de animação <50KB gzip total
- [ ] Testado em Chrome + Safari + Firefox + mobile real
- [ ] `will-change` removido após animação concluída
- [ ] Lottie peso <100KB
- [ ] Hero animation visível em 3s (LCP friendly)
- [ ] Sem `animation: infinite` desnecessário (drena bateria mobile)

## Anti-patterns

❌ Animar `top`/`left`/`width`/`height` (use `transform`)
❌ Esquecer `prefers-reduced-motion` (a11y)
❌ Hero animation atrasando LCP
❌ GSAP carregado pra fazer um fade simples (overkill)
❌ Lottie 2MB no projeto (otimizar com `lottie-cli`)
❌ Scroll-jacking com JS (use scroll-behavior nativo + GSAP scrub se preciso)
❌ Animação rodando offscreen (use IntersectionObserver pra pausar)
❌ Multiple animation libs (escolher UMA stack)
❌ `transition: all` (especifique propriedades)
❌ Loops infinitos sem motivo (battery drain)

## Skills relacionadas

- **`motion-design-system`** (owned @web-designer) — 12 leis universais de motion
- **`remotion-advanced`** (owned @web-designer) — vídeo programático React
- **`hyperframes-integration`** (owned @web-designer) — HTML→Video
- **`lottie-integration`** (owned @web-designer) — Lottie em vídeo + web
- **`frontend-design`** (.claude/skills) — production interfaces
- **`ui-design-system`** (.claude/skills) — design tokens

## Referências

- View Timeline API: https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timeline
- GSAP: https://gsap.com/docs/
- Framer Motion: https://www.framer.com/motion/
- Motion One: https://motion.dev
- Lottie Web: https://airbnb.io/lottie/#/web
- Web.dev animations: https://web.dev/animations-guide/
