prefers-reduced-motion: motion acessível sem matar o design
A palavra da especificação é reduzir, não remover. Por que animation: none !important erra dos dois lados, e a tabela de substituição que preserva o design.
- acessibilidade
- motion design

Sumário
A palavra da especificação é reduzir, não remover.
A MDN lista três saídas: a interface pode remover, reduzir ou substituir a animação. A terceira é a que quase ninguém implementa.
A solução que circula é jogar animation: none !important em tudo. Ela erra dos dois lados.
Apaga o que comunica estado, como o spinner que diz que o sistema está trabalhando. E não encosta no parallax em JavaScript, que é o que de fato causa o problema.
O que de fato faz mal
Antes de decidir o que desligar, vale saber o que se está evitando, porque a resposta muda o alvo.
A finalidade documentada da preferência é atender pessoas com distúrbios vestibulares, do ouvido interno. O W3C descreve as reações como severas: tontura, náusea, enxaqueca, perda de foco, e casos em que a pessoa precisa ficar de cama para se recuperar. A MDN é específica sobre o gatilho: escalar ou deslocar objetos grandes provoca desconforto.
Daí sai um critério prático que não aparece na maior parte dos tutoriais:
| Padrão | Risco | Por quê |
|---|---|---|
| Parallax e movimento de fundo | Alto | Área grande se deslocando fora de sincronia com o gesto |
| Zoom e escala de elemento grande | Alto | Simula aproximação, engana o sistema vestibular |
| Rotação e giro contínuo | Alto | Movimento sem ponto de repouso |
| Deslizar seção inteira | Médio | Depende da área e da distância |
| Deslocamento pequeno, poucos pixels | Baixo | Área pequena, duração curta |
| Fade de opacidade | Baixo | Não há movimento, só variação de luminosidade |
| Mudança de cor | Baixo | Sem componente espacial |
A última linha é a chave do post inteiro. Se opacidade e cor são seguras, você não precisa entregar uma página estática para quem ligou a preferência. Precisa entregar a mesma página com outro vocabulário de transição.
A sintaxe, e a escolha que ela força
Dois valores, e a diferença entre eles não é simétrica.
/* a pessoa ligou a preferência no sistema */
@media (prefers-reduced-motion: reduce) { }
/* ninguém declarou preferência; avalia como falso quando reduce está ligado */
@media (prefers-reduced-motion: no-preference) { }
/* forma curta, equivalente a reduce */
@media (prefers-reduced-motion) { }Isso força uma escolha de arquitetura que dá para inverter:
Padrão opt-out. Você anima por padrão e desliga dentro de reduce. É o mais comum e o mais frágil: num navegador que não suporta a consulta, o bloco de desligamento nunca roda e a animação toca.
Padrão opt-in. Você escreve a animação inteira dentro de no-preference. O estado sem animação vira o padrão, e o movimento é o acréscimo.
.card {
opacity: 0;
transform: translateY(24px);
}
/* sem suporte à consulta, este bloco não se aplica e nada anima */
@media (prefers-reduced-motion: no-preference) {
.card {
transition: opacity .5s ease, transform .5s ease;
}
.card.visivel { opacity: 1; transform: none; }
}Um cuidado no exemplo acima. O estado inicial esconde o elemento, então a revelação depende da animação rodar. Sem ela, o conteúdo some.
O jeito seguro inverte isso: o estado final é o padrão, e a animação só decora a chegada. Ela nunca é o que torna o conteúdo visível.
Por que animation: none !important é a resposta errada
A receita curta que circula:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation: none !important;
transition: none !important;
}
}Três problemas concretos, em ordem de gravidade.
Ela apaga animação que carrega informação. Spinner de carregamento, barra de progresso, indicador de gravação: todos comunicam estado por movimento. Desligados, a pessoa fica sem saber se o sistema travou. Tanto o critério 2.3.3 quanto o 2.2.2 do WCAG abrem exceção explícita para animação essencial à funcionalidade ou à informação transmitida, e essa exceção existe por esse motivo.
Ela não alcança o que mais machuca. Parallax feito com transform em JavaScript, animação de scroll com ScrollTrigger, vídeo de fundo em autoplay: nada disso é animation nem transition de CSS. O seletor universal passa longe do problema real.
Ela entrega uma experiência pior sem precisar. A pessoa pediu menos movimento, não menos design. Zerar toda transição faz a interface responder com trancos, o que piora a percepção de qualidade sem ganho de acessibilidade.
Existe uma variante menos ruim. Ela troca none por uma duração mínima em vez de cortar. Assim os eventos de fim de animação continuam disparando, e o JavaScript que depende deles não quebra:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
transition-duration: .01ms !important;
}
}Ainda é remédio de emergência para site legado. Para código novo, substitua em vez de cortar.
Substituir, não remover
A tabela que eu uso como referência de projeto:
| Efeito original | Substituição sob reduce |
|---|---|
| Entrada deslizando de baixo | Fade de opacidade, no lugar |
| Parallax de fundo | Fundo estático, sem deslocamento |
| Zoom em imagem no hover | Mudança de brilho ou de borda |
| Carrossel com auto-avanço | Sem auto-avanço, só controle manual |
| Contador animado | Valor final, direto |
| Transição de página deslizante | Corte seco ou fade curto |
| Vídeo de fundo em autoplay | Primeiro quadro como imagem estática |
| Rolagem suave | Salto direto ao destino |
O princípio: preserve o significado, troque o veículo. Se o slide para cima dizia "isto é novo", o fade diz a mesma coisa sem deslocar área grande.
O lado JavaScript
CSS não alcança tudo. Para animação em código, a mesma consulta funciona via matchMedia, e o evento de mudança importa mais do que parece:
const consulta = window.matchMedia("(prefers-reduced-motion: reduce)");
function aplicar() {
if (consulta.matches) {
pararParallax();
document.querySelectorAll("video[autoplay]").forEach(v => v.pause());
} else {
ligarParallax();
}
}
aplicar();
consulta.addEventListener("change", aplicar);Ler consulta.matches uma vez, no carregamento, é o erro mais comum. A pessoa pode ligar a preferência no meio da sessão, e sem o ouvinte de change a página continua se mexendo até um recarregamento.
Nas duas bibliotecas que dominam o mercado, o idioma já existe.
GSAP trata a preferência como mais uma condição no gsap.matchMedia(). Ele reverte sozinho o que foi criado ali dentro, animação e ScrollTrigger:
const mm = gsap.matchMedia();
mm.add({
reduzir: "(prefers-reduced-motion: reduce)",
normal: "(prefers-reduced-motion: no-preference)",
}, (ctx) => {
const { reduzir } = ctx.conditions;
gsap.from(".secao", {
opacity: 0,
y: reduzir ? 0 : 60,
duration: reduzir ? 0.2 : 0.8,
scrollTrigger: { trigger: ".secao", start: "top 85%" },
});
});Motion, a antiga Framer Motion, resolve no nível da aplicação com MotionConfig. Com reducedMotion="user", ela desativa animações de transform e de layout e mantém opacidade e cor de fundo, que é exatamente a substituição recomendada:
<MotionConfig reducedMotion="user">
<App />
</MotionConfig>Os valores aceitos são "user", "always" e "never". Para controle fino, o hook useReducedMotion() devolve um booleano e deixa você escolher a substituição caso a caso, como trocar x: "-100%" por opacity: 0 numa gaveta lateral.
Os oito padrões de ScrollTrigger, incluindo esse recorte responsivo, estão em ScrollTrigger: os 8 padrões.
Quatro pontos que quase todo site esquece
Rolagem suave. scroll-behavior: smooth é a omissão mais frequente, e a documentação não promete tratamento automático: a MDN registra apenas que o navegador pode ignorar a propriedade. Não conte com isso, declare a intenção:
@media (prefers-reduced-motion: no-preference) {
html { scroll-behavior: smooth; }
}Vídeo de fundo com autoplay. Não é animation nem transition, então nenhum reset de CSS o alcança. Precisa de JavaScript, ou de trocar o vídeo por um poster estático.
Transições de página. As View Transitions animam por padrão, e a mesma consulta desliga a animação preservando a troca de conteúdo. O funcionamento delas está em View Transitions em Next.js 16.
Animações scroll-driven em CSS. Rodam na thread do compositor e não passam por JavaScript, o que é ótimo para desempenho e significa que só a consulta de mídia as controla. Como elas funcionam está em CSS scroll-driven sem JS.
O que o WCAG exige, e o que não exige
Vale separar os dois critérios, porque o nível de conformidade é diferente e isso muda a prioridade.
| Critério | Nível | Cobre |
|---|---|---|
| 2.2.2 Pause, Stop, Hide | A | Movimento automático que dura mais de cinco segundos, ao lado de outro conteúdo |
| 2.3.3 Animation from Interactions | AAA | Animação de movimento disparada por interação, como parallax de rolagem |
O 2.2.2 é nível A, o piso da conformidade, e exige um mecanismo de pausar, parar ou esconder. Carrossel com auto-avanço e vídeo de fundo em loop caem aqui.
O 2.3.3, nível AAA, é onde mora o parallax. Ele diz que a animação disparada por interação precisa poder ser desativada. A exceção é a mesma: animação essencial à funcionalidade ou à informação transmitida.
Duas leituras práticas.
Respeitar prefers-reduced-motion é o caminho mais curto para o 2.3.3. O sistema operacional já é o mecanismo de desativação que o critério pede.
O 2.2.2 não se resolve só com a consulta de mídia. Ele pede um controle visível na interface, para quem não ligou a preferência no sistema.
Como testar
Ligue a preferência e recarregue. Em navegador Chromium também dá para emular pelo DevTools, em Rendering, na opção de emular a mídia prefers-reduced-motion.
| Sistema | Caminho |
|---|---|
| Windows 11 | Configurações → Acessibilidade → Efeitos visuais → Efeitos de animação |
| Windows 10 | Configurações → Facilidade de Acesso → Vídeo → Mostrar animações |
| macOS | Ajustes do Sistema → Acessibilidade → Tela → Reduzir movimento |
| iOS | Ajustes → Acessibilidade → Movimento |
| Android 9+ | Configurações → Acessibilidade → Remover animações |
| GNOME | Configurações → Acessibilidade → Visão → Animação reduzida |
Teste a página inteira com a preferência ligada. Três perguntas: todo o conteúdo aparece? Os estados continuam legíveis? Algo ficou preso invisível?
O terceiro é o defeito mais comum de quem implementa pela primeira vez.
O contexto maior, do briefing à entrega, está no guia completo de motion design para web. E se a dúvida é qual ferramenta usar antes de chegar nesse ponto, a árvore de decisão entre GSAP, Motion e CSS resolve em três perguntas.
Perguntas frequentes
Qual a diferença entre reduce e no-preference?
reduce indica que a pessoa ligou a configuração de movimento reduzido no dispositivo. no-preference indica que nenhuma preferência foi declarada e avalia como falso quando reduce está ativo. Escrever a animação dentro de no-preference deixa o estado sem movimento como padrão, inclusive em navegador que não suporta a consulta.
Preciso desligar toda animação quando a preferência está ativa?
Não. A especificação fala em remover, reduzir ou substituir, e animação essencial à funcionalidade ou à informação tem exceção explícita nos dois critérios do WCAG. Fade de opacidade e mudança de cor não têm componente espacial e costumam ser seguros como substituição.
O scroll-behavior: smooth respeita a preferência sozinho?
A documentação não garante isso. A MDN registra que o navegador pode ignorar a propriedade, sem prometer tratamento de movimento reduzido. Coloque a declaração dentro de @media (prefers-reduced-motion: no-preference) em vez de contar com o comportamento do navegador.
Isso é obrigatório para conformidade?
Depende do nível. Animação automática que dura mais de cinco segundos cai no critério 2.2.2, nível A, que é o piso de conformidade e pede um controle de pausar, parar ou esconder. Animação disparada por interação, como parallax, cai no 2.3.3, nível AAA. Respeitar a preferência do sistema atende ao segundo e ajuda no primeiro, sem substituir o controle na interface.
Fontes
- MDN Web Docs — prefers-reduced-motion. Acesso em 20/08/2026.
- MDN Web Docs — scroll-behavior. Acesso em 20/08/2026.
- W3C — Understanding SC 2.3.3: Animation from Interactions. Acesso em 20/08/2026.
- W3C — Web Content Accessibility Guidelines 2.2. Acesso em 20/08/2026.
- Motion — Acessibilidade e movimento reduzido. Acesso em 20/08/2026.
- GSAP — gsap.matchMedia()/). Acesso em 20/08/2026.
Verificado em 20 de agosto de 2026.
Gatilho de reavaliação: revisar quando (a) a especificação de mídia ganhar valor além de reduce e no-preference, (b) os navegadores passarem a documentar tratamento automático de scroll-behavior sob movimento reduzido, ou (c) o WCAG promover o critério 2.3.3 para um nível abaixo de AAA.
Leia também
Motion •
Animação de texto: SplitText, clip-path e o que o CSS já faz
Metade dos efeitos de texto não precisa quebrar o texto em pedaços. A pergunta que separa as duas famílias, e o que o SplitText resolve que o CSS não resolve.
- motion design
- css
Motion •
ScrollTrigger: os 8 padrões que cobrem 90% dos casos
Oito receitas de ScrollTrigger com o código mínimo de cada uma, os padrões que quase ninguém lê na doc e o que quebra quando você põe tudo dentro do React.
- gsap
- scrolltrigger
Motion •
GSAP, Motion ou CSS: a árvore de decisão
A pergunta mudou: metade do que exigia biblioteca hoje é CSS nativo. Os eixos que sobraram são licença, modelo de execução e o que já é nativo.
- gsap
- motion


