Claude Code e Framer Motion: guia prático de Motion for React
Use Claude Code com Framer Motion/Motion for React: prompts, exemplos TSX, armadilhas, acessibilidade e verificação.
Muita gente ainda pesquisa por Framer Motion, mas a documentação oficial atual apresenta a biblioteca como Motion for React. Em código novo, instale motion e importe as APIs de React a partir de motion/react. Se você mantém uma aplicação antiga que já padronizou framer-motion, não misture os dois estilos sem uma decisão clara. Para novas implementações, use a documentação oficial de Motion for React como referência.
Claude Code acelera bastante o trabalho de animação, mas não substitui critério de produto. Um prompt como “deixe isso mais suave” pode gerar movimento exagerado, ignorar Reduced Motion ou quebrar animações de saída porque o componente pai desmonta cedo demais. Masa testou esse fluxo em um dashboard interno: a primeira versão parecia boa em uma demo, mas os cards pulavam demais, as notificações sumiam sem exit e o cabeçalho ligado ao scroll ficava pesado em um notebook simples.
A abordagem prática é tratar Claude Code como colaborador com limites. Primeiro defina intenção, escopo, acessibilidade e verificação; depois peça o código. Os exemplos TSX abaixo funcionam em um projeto React após npm install motion. Em Next.js ou Astro, deixe esses componentes no lado cliente.
Comece pelas restrições
Animação não é decoração. Ela deve explicar uma mudança de estado. Antes de pedir a alteração, escreva as restrições.
| Decisão | O que pedir ao Claude Code | O que revisar |
|---|---|---|
| Objetivo | Qual mudança de estado precisa de ajuda | A animação melhora a compreensão |
| Escopo | Arquivos permitidos e proibidos | Não há refatoração fora do tema |
| API | motion/react e Reduced Motion | Imports e comportamento seguem a documentação |
| Verificação | Uso manual, teclado, dispositivo lento | O teste parece uso real |
O fluxo pode ser visto assim:
Intenção e restrições
-> prompt para Claude Code
-> implementação Motion for React
-> verificação manual e de acessibilidade
-> prompt de ajuste focado
Um primeiro prompt útil:
Adicione animação com Motion for React a este componente React existente.
Requisitos:
- Importar novas APIs de `motion/react`
- Não mudar fetch de dados, submit de formulário nem routing
- Respeitar Reduced Motion
- Preferir opacity e transform
- Evitar layout shift
- Retornar uma checklist curta de testes manuais
Esse nível de detalhe evita que Claude Code copie exemplos antigos com framer-motion. A origem do import deve ser uma regra explícita do projeto.
Caso 1: lista de cards animada
Dashboards, listas de artigos e cards de recursos se beneficiam de um stagger discreto. O objetivo não é fazer cada card se exibir, mas criar uma ordem natural de leitura. No teste de Masa, 0,06 a 0,09 segundo funcionou bem para seis cards; mais do que isso atrasou o início da interação.
import { motion, useReducedMotion } from "motion/react";
type Feature = {
id: string;
title: string;
body: string;
metric: string;
};
const demoFeatures: Feature[] = [
{
id: "review",
title: "Fila de revisão",
body: "Mudanças geradas por Claude Code que uma pessoa precisa revisar.",
metric: "8 itens",
},
{
id: "motion",
title: "Ajuste de movimento",
body: "Transições curtas explicam mudanças sem desacelerar o trabalho.",
metric: "14%",
},
{
id: "a11y",
title: "Reduced Motion",
body: "Movimentos grandes viram um fade mais calmo.",
metric: "Pronto",
},
];
export function AnimatedFeatureCards({
items = demoFeatures,
}: {
items?: Feature[];
}) {
const shouldReduceMotion = useReducedMotion();
const container = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: shouldReduceMotion ? 0 : 0.08,
},
},
};
const card = {
hidden: {
opacity: 0,
y: shouldReduceMotion ? 0 : 16,
},
visible: {
opacity: 1,
y: 0,
transition: {
duration: 0.32,
ease: "easeOut",
},
},
};
return (
<motion.section
aria-label="Cards de recursos"
variants={container}
initial="hidden"
animate="visible"
style={{
display: "grid",
gap: 16,
gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
}}
>
{items.map((item) => (
<motion.article
key={item.id}
variants={card}
whileHover={shouldReduceMotion ? undefined : { y: -4 }}
style={{
border: "1px solid #d9e2ec",
borderRadius: 12,
padding: 20,
background: "#ffffff",
boxShadow: "0 8px 24px rgba(15, 23, 42, 0.08)",
}}
>
<p style={{ margin: 0, color: "#2563eb", fontWeight: 700 }}>
{item.metric}
</p>
<h3 style={{ margin: "8px 0", fontSize: 18 }}>{item.title}</h3>
<p style={{ margin: 0, lineHeight: 1.7, color: "#475569" }}>
{item.body}
</p>
</motion.article>
))}
</motion.section>
);
}
Revise chaves estáveis, movimento baseado em transform e comportamento com Reduced Motion. Um bom prompt de continuação é: “Mantenha o stagger, mas faça a lista continuar utilizável imediatamente com 12 cards”.
Caso 2: notificações com saída
AnimatePresence permite animar elementos quando saem da árvore React. A documentação de AnimatePresence destaca que filhos diretos precisam de key estável. O erro comum é renderizar condicionalmente o contêiner inteiro, desmontando o pai antes do exit dos filhos.
import { useState } from "react";
import { AnimatePresence, motion } from "motion/react";
type Toast = {
id: number;
message: string;
};
let nextToastId = 1;
export function AnimatedNotifications() {
const [toasts, setToasts] = useState<Toast[]>([
{ id: 0, message: "Salvo com sucesso" },
]);
function addToast() {
const id = nextToastId++;
setToasts((current) => [
...current,
{ id, message: `Tarefa em segundo plano ${id} concluída` },
]);
}
function dismissToast(id: number) {
setToasts((current) => current.filter((toast) => toast.id !== id));
}
return (
<div>
<button type="button" onClick={addToast}>
Adicionar notificação
</button>
<div
aria-live="polite"
style={{
position: "fixed",
right: 24,
top: 24,
display: "grid",
gap: 12,
width: "min(360px, calc(100vw - 48px))",
}}
>
<AnimatePresence initial={false} mode="popLayout">
{toasts.map((toast) => (
<motion.div
layout
key={toast.id}
initial={{ opacity: 0, x: 40, scale: 0.96 }}
animate={{ opacity: 1, x: 0, scale: 1 }}
exit={{ opacity: 0, x: 40, scale: 0.96 }}
transition={{ duration: 0.2, ease: "easeOut" }}
style={{
borderRadius: 10,
border: "1px solid #cbd5e1",
background: "#ffffff",
padding: 16,
boxShadow: "0 12px 30px rgba(15, 23, 42, 0.16)",
}}
>
<p style={{ margin: "0 0 12px", color: "#0f172a" }}>
{toast.message}
</p>
<button type="button" onClick={() => dismissToast(toast.id)}>
Fechar
</button>
</motion.div>
))}
</AnimatePresence>
</div>
</div>
);
}
Peça a Claude Code para confirmar se o contêiner continua montado quando não há notificações, se o fechamento funciona pelo teclado e se aria-live não anuncia demais. Notificação é um componente repetido; silêncio e previsibilidade valem mais que efeito.
Caso 3: progresso de leitura com scroll
Em guias longos, onboarding e landing pages, um indicador de progresso ajuda o leitor a se orientar. useScroll retorna o progresso da página ou de um elemento como MotionValue. Com useSpring, a barra fica menos tremida. Não transforme cada seção em parallax; com Reduced Motion, remova grandes deslocamentos.
import { useRef } from "react";
import {
motion,
useReducedMotion,
useScroll,
useSpring,
useTransform,
} from "motion/react";
const sections = [
{
title: "Fixar o requisito",
body: "Antes de editar, Claude Code precisa conhecer objetivo, arquivos e limites.",
},
{
title: "Mover menos",
body: "Comece com opacity e transform para apoiar a compreensão.",
},
{
title: "Verificar caminhos reais",
body: "Teste dispositivo lento, teclado e Reduced Motion antes de publicar.",
},
];
export function ScrollReadingProgress() {
const articleRef = useRef<HTMLElement | null>(null);
const shouldReduceMotion = useReducedMotion();
const { scrollYProgress } = useScroll({
target: articleRef,
offset: ["start start", "end end"],
});
const scaleX = useSpring(scrollYProgress, {
stiffness: 120,
damping: 28,
mass: 0.2,
});
const y = useTransform(scrollYProgress, [0, 1], [0, -48]);
return (
<article ref={articleRef} style={{ position: "relative", padding: 24 }}>
<motion.div
aria-hidden="true"
style={{
position: "sticky",
top: 0,
zIndex: 10,
height: 4,
scaleX: shouldReduceMotion ? 1 : scaleX,
transformOrigin: "0% 50%",
background: "#2563eb",
}}
/>
<motion.header
style={{
y: shouldReduceMotion ? 0 : y,
padding: "56px 0 32px",
}}
>
<p style={{ color: "#2563eb", fontWeight: 700 }}>
Claude Code x Motion
</p>
<h2 style={{ fontSize: 36, margin: 0 }}>
Uma página que revela contexto durante a leitura
</h2>
</motion.header>
<div style={{ display: "grid", gap: 24 }}>
{sections.map((section) => (
<section
key={section.title}
style={{
border: "1px solid #dbe4ee",
borderRadius: 12,
padding: 24,
background: "#ffffff",
}}
>
<h3>{section.title}</h3>
<p style={{ lineHeight: 1.8 }}>{section.body}</p>
</section>
))}
</div>
</article>
);
}
Use esse padrão para orientar, não para preencher espaço. Uma barra de progresso clara normalmente ajuda mais do que várias revelações por scroll que deixam a página lenta.
Faça Claude Code revisar o diff
Depois da implementação, peça uma revisão crítica com falhas específicas.
Revise o diff atual como uma implementação Motion for React.
Seja rigoroso com:
- Não misturar `motion/react` com imports antigos de `framer-motion`
- Filhos diretos de `AnimatePresence` têm key estável
- Reduced Motion desativa grandes movimentos, parallax e efeitos tipo autoplay
- O código não anima frequentemente width, height, top ou left
- Os testes manuais representam ações reais de usuário
Esse prompt transforma Claude Code em uma primeira camada de revisão. Como ele pode ler arquivos próximos, também consegue apontar conflitos de CSS, mudanças indesejadas de estado e testes ausentes.
Armadilhas e checklist
A primeira armadilha é import antigo. Em código legado, manter framer-motion pode ser correto, mas código novo deve seguir motion e motion/react salvo decisão contrária do repositório. A segunda é depurar exit como se fosse problema de easing. Normalmente é problema da árvore React: o pai desmonta cedo, a key é índice ou o item não sai dentro de AnimatePresence.
A terceira é confiar demais em layout. Ele ajuda em reordenação e mudança de tamanho, mas imagens sem dimensões, fontes carregadas tarde e mudanças bruscas de Grid ainda geram saltos. A quarta é deixar acessibilidade para o fim. O guia oficial de acessibilidade explica Reduced Motion; se há grandes movimentos, parallax ou autoplay, a alternativa calma deve existir na primeira versão.
Antes de publicar, confirme: documentação oficial e imports estão alinhados, cada animação tem propósito, Reduced Motion remove movimentos grandes, filhos de AnimatePresence têm key, teclado e foco continuam funcionando, efeitos de scroll não prejudicam leitura.
Para bases de UI, leia Radix UI com Claude Code, shadcn/ui com Claude Code e o guia de animação com Claude Code. Sobre o agente, consulte a documentação do Claude Code da Anthropic.
Conclusão
Combinar Claude Code com Framer Motion não vale apenas por escrever animações mais rápido. O valor real é colocar implementação, acessibilidade e revisão no mesmo fluxo antes de publicar.
O resultado prático de Masa foi claro: escrever motion/react, Reduced Motion e verificação manual no primeiro prompt reduziu retrabalho mais do que qualquer polimento posterior. Para guardar o modelo, use a folha de dicas gratuita. Para revisar várias telas com um time, a página de treinamento e consultoria é o próximo passo prático.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.