Motion vs CSS: quando usar cada um (arvore de decisao)
8 min de leitura
Voce terminou css-animacoes. Sabe fazer
transition: opacity 0.3s ease, sabe
@keyframes spin pra loading, sabe que
transform: translateX e GPU-accelerated. Aí
voce precisa animar uma lista reordenando
items, ou fazer exit animation quando um
modal fecha, ou triggar animacao quando o
usuario rola a pagina. CSS nao resolve esses
casos sem gambiarra. Aí entra Motion.
A pergunta deste no e simples: quando usar CSS e quando usar Motion? A resposta nao e "um ou outro" - e uma arvore de decisao baseada no que a animacao precisa fazer.
O essencial 🟢
A pergunta-chave: "a animacao precisa de estado ou sequencia complexa?" Se sim, Motion. Se nao, CSS. Detalhando:
| Caso | Ferramenta | Por que |
|---|---|---|
| Hover em link, focus em input, transicao simples | CSS | Stateless, declarativo, GPU-accelerated |
| Modal aparece/some, toast entra/sai | Motion | Mount/unmount precisa de exit animation |
| Items de lista reordenam | Motion | FLIP animation (shared layout) |
| Drag, swipe, pan com gesto do usuario | Motion | whileDrag + dragConstraints |
| Animacao triggered por scroll position | Motion | useScroll + useTransform |
| Animacao de SVG path complexa | Motion ou Lottie | Motion: morphing. Lottie: vetor pre-feito. |
| Mascote / ilustracao vetorial com varios estados | Rive | Animacao complexa feita no Figma/After Effects |
| Theme toggle (light/dark) com cross-page transition | View Transitions | API nativa do browser, sem JS |
A regra pratica: CSS primeiro, Motion quando CSS nao resolve. A maioria das animacoes (~80%) cabem em CSS. Motion entra nos 20% que CSS nao cobre bem.
Por que CSS nao cobre esses 20%. O motivo e fundamental: CSS animation e stateless. Voce define keyframes e o browser toca. Quando o elemento sai da arvore DOM (ex: modal fecha), CSS nao tem mecanismo de "exit animation" - o elemento e removido imediatamente. Solucoes com CSS (delay na remocao, etc) viram gambiarra.
Motion e stateful. O <motion.div> sabe
quando esta mountando, animando, ou saindo. Da
pra definir exit={{ opacity: 0 }} e o Motion
espera a animacao terminar antes de remover do
DOM. Sem gambiarra.
A arvore de decisao visualizada:
A leitura e: o criterio de decisao e o que a animacao precisa fazer, nao qual ferramenta voce prefere. A maioria dos devs pensa "Motion em tudo" (porque e mais legal) ou "CSS em tudo" (porque e mais simples). A resposta certa e: CSS pro simples, Motion pro stateful, View Transitions pra cross-page, Rive/Lottie pra assets do design.
Performance: a preocupacao que nao importa (ainda). Voce vai ouvir "Motion e mais lento que CSS". Tecnicamente sim - Motion roda JS, CSS e GPU-accelerated nativo. Na pratica, a diferenca e imperceptivel em casos reais:
- CSS animation: roda na compositor thread (GPU). 60fps garantido.
- Motion: roda na main thread (JS). Pode cair pra 30fps em animacoes muito complexas (muitos elementos simultaneos).
Em hardware moderno (celular mid 2024+), Motion roda 60fps ate ~30 elementos animando ao mesmo tempo. Acima disso, voce tera problemas - e mesmo com CSS, 30 elementos animados ao mesmo tempo e problema (pinta muito).
A regra pratica: use Motion sem medo ate ter evidencia de problema. Profile depois. Em landing page tipica (5-10 elementos animando na entrada), Motion roda 60fps. Em dashboard denso (100+ rows animando reordenacao), voce tem problema de qualquer jeito (virtualize a lista primeiro).
prefers-reduced-motion - o filtro universal.
Todo no de animacao desta trilha termina com:
respeite prefers-reduced-motion. E o CSS
media query que detecta se o usuario configurou
o SO pra reduzir movimento (sensibilidade
vestibular, migranea, TDAH, ou simplesmente
preferência pessoal). Browsers e SOs tem essa
opcao em Settings > Accessibility.
/* CSS - respeitando reduced motion */
.button {
transition: transform 0.2s ease;
}
@media (prefers-reduced-motion: reduce) {
.button {
transition: none;
}
}
// Motion - useReducedMotion
import { useReducedMotion } from "motion/react";
function AnimatedButton() {
const reduced = useReducedMotion();
return (
<motion.button
whileHover={reduced ? undefined : { scale: 1.05 }}
/>
);
}
Toda animacao desta trilha tem esse pattern:
se reduced, ou desabilita a animacao, ou usa
uma versao simplificada (fade em vez de scale +
translate). Motion 11+ tem MotionConfig que
faz isso globalmente:
import { MotionConfig } from "motion/react";
<MotionConfig reducedMotion="user">
{/* Todas as animacoes respeitam prefers-reduced-motion */}
<App />
</MotionConfig>
reducedMotion="user" significa "se o usuario
preferir reduced motion, simplifica todas as
animacoes". O Motion internamente reduz
duração, troca springs por tweens, e remove
effects como parallax. Voce nao precisa
lembrar em cada componente.
Aprofundamento 🟡
O detalhe de GPU acceleration. Pra uma
animacao rodar a 60fps sem jank, ela precisa
ser pintada pela GPU. CSS consegue isso
quando voce anima transform (translate, scale,
rotate) e opacity - essas propriedades nao
causam layout, nao causam paint, so "composicao"
(compositor thread). Animar width, height,
top, left causa layout + paint - nao roda
na GPU, janka em 30+ elementos.
Motion respeita isso por baixo - anima transform
e opacity por default. Mas se voce mexer em
width via Motion (com animate={{ width: 100 }}),
voce quebra a GPU acceleration. Regra: anime
transform e opacity. Mais nada, a nao ser
que voce saiba o que esta fazendo.
Spring physics: a diferenca filosofica. CSS
tem cubic-bezier() e ease/linear - funcoes
matematicas. Motion tem spring - simulacao
fisica de mola. A diferenca pratica:
- CSS
ease: comeca rapido, desacelera, para (animacao previsivel). - Motion
spring: passa do alvo, volta, ajusta, repousa (animacao organica, humana).
Em interface, spring da sensacao de "vivo" - botao que "pula" ao clicar, card que "pousa" ao entrar. Ease da sensacao de "mecanico". Motion default e spring, e isso e por que parece natural.
Pra usar linear ou tween (ease equivalente)
em Motion: transition={{ type: "tween", duration: 0.3, ease: "easeOut" }}. Use quando voce quer
comportamento previsivel (loading bars, progress).
Reduced motion: simplifique, nao desabilite.
A primeira reacao a prefers-reduced-motion e
"desabilita toda animacao". Isso quebra UX:
modal aparece instantaneamente (jarring), toast
sem feedback, hover sem resposta. O usuario
preferiu menos movimento, nao zero
movimento.
A pratica recomendada (WCAG 2.3.3 + WAI guidance): substitua movimento por fade. Em vez de "elemento entra com scale + translate + rotate
- spring 800ms", use "elemento entra com opacity 0 -> 1 em 200ms". Menos movimento, mesma informacao.
// "Sem reduced motion" - completo
<motion.div
initial={{ opacity: 0, scale: 0.8, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
transition={{ type: "spring", stiffness: 300 }}
/>
// "Com reduced motion" - simplificado
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
/>
MotionConfig reducedMotion="user" faz essa
substituicao automaticamente - ele nao remove
animacao, ele substitui movimento grande
por fade. Boa pratica default.
Layout animations com FLIP. A tecnica "FLIP"
(First, Last, Invert, Play) e o coracao do
layout prop do Motion. Explicado:
- First: mede posicao do elemento ANTES da mudanca.
- Last: aplica a mudanca, mede posicao DEPOIS.
- Invert: aplica
transformpra "voltar" visualmente pra posicao antiga. - Play: anima
transformde "voltar" pra "natural" (sem transform).
O usuario ve: elemento desliza suavemente da
posicao antiga pra nova, sem "pular". Motion
faz isso internamente, voce so coloca layout
no <motion.div>. Coberto a fundo no no 3.
Quando JS vale o custo. Em resumo:
- Vale JS (Motion): exit animation, AnimatePresence, gestural, layout, scroll- linked, sequenciamento complexo.
- Nao vale JS (CSS): hover simples, focus, loading spinners, transicoes de cor/opacity/ transform sem unmount.
A pegadinha: animacoes que dependem de estado JS (ex: "animar quando item e adicionado a lista") sao impossiveis em CSS sem workarounds (classes condicionais + key trick). Motion resolve isso nativamente. Da vem o "vale JS" - nao performance, mas expressividade.
Pra quem quer ir alem 🔴
Historia em uma linha. CSS animation nasceu
com CSS3 (~2009, com transition e @keyframes).
Em 2014, Velocity.js virou popular pra "CSS
- JS fallback". Em 2015, GSAP (GreenSock) dominou animacao JS complexa (timeline, sequenciamento). Em 2017, Framer Motion surgiu como "GSAP pra React" - mesma API declarativa, integrada com React. Em 2024, Framer Motion virou Motion (renomeado pela equipe, mesmo time). Em 2026, Motion e o padrao de fato em React, GSAP ainda existe mas perdeu espaco. Rive e Lottie ocupam nicho de assets vetoriais do design.
Motion One: a versao core. Motion 11+ tambem publica Motion One - a versao "core" sem dependencia de React. 1.7KB gzipped, funciona com qualquer framework (ou vanilla JS). Mesma API basica (animate, in-view, scroll). Vale pra landing pages estaticas que nao usam React. Em 2026, e o "CSS++" - mais leve que Motion+React, mais poderoso que CSS-only.
Por que Motion venceu GSAP no React. GSAP
existia antes e e mais maduro em features
(timeline complexa, morphing de path SVG, etc).
Motion venceu por integracao com React:
declarativo (renderiza com JSX), ergonomico
(variants, props), e respeita lifecycle do React
(cleanup, re-render). GSAP precisa de useRef +
useEffect + cleanup manual. Em React, Motion e
o caminho natural. Em vanilla JS ou outros
frameworks, GSAP ainda e relevante.
Performance real: profiling. Em 2026, o melhor profiler e o DevTools Performance tab do Chrome. Profile com 6x CPU throttling (simula celular mid). Animate 30+ elementos simultaneamente. Se o FPS fica abaixo de 60, simplifique. As otimizacoes comuns:
will-change: transform- diz ao browser pra promover o elemento pra sua propria compositor layer. Use com moderacao (cada layer custa memoria).- Lazy load Motion - se voce so usa em
uma landing page,
dynamic(() => import('motion/react'))no Next.js. - Simplificar variantes - 5 variants em um mesmo componente = 5 vezes mais JS por render.
- Evitar
layoutem listas grandes - o FLIP faz layout em cada item; 100 items = 100 FLIPs.
Leitura recomendada:
- Motion - Quick Start - a doc oficial, hands-on em 5 min.
- CSS Tricks - Animation Performance - o "por que" de GPU acceleration.
- MDN - prefers-reduced-motion - a referencia da media query.
Dica: o erro mais comum no comeco e importar Motion pra tudo. "Vou animar o hover do botao com Motion" - nao. Use CSS. CSS e 0KB de JS, GPU-accelerated, e nao precisa de preferencia de movimento no codigo (o media query e uma linha). Reserve Motion pro que CSS nao resolve (mount/unmount, layout, gestures).
No proximo no, vamos abrir o Motion basico:
motion.div, AnimatePresence, variants, e o
ciclo de vida mount/animate/exit.
// Quiz
Qual destas animacoes deve ser feita com CSS e nao com Motion?