Capítulo 34
Animações CSS
5 min de leitura 935 palavras 2 gravuras
Esta é o capítulo 34 de 52 do volume Aprenda a Web. O projeto-fio é a Oficina Costeira, um coletivo fictício de design em João Pessoa. O exemplo está em exemplos/34-animacoes-css/. Em cada capítulo: leia, copie, mude uma coisa, preveja, recarregue e confira.
O que você vai aprender
- Escolher
transitionouanimation. - Animar cor, transformação e opacidade.
- Escrever
@keyframescom nome estável. - Respeitar
prefers-reduced-motion. - Evitar movimento que enjoa ou esconde conteúdo.
- Testar hover e teclado no mesmo controle.
Transição ou animação
Transição tem começo e fim. A pessoa passa o mouse ou o foco, a cor muda em 200 ms. Animação tem vários estágios e pode começar sozinha, em loop.
Se só há dois estados, use transition. Se o selo gira ao carregar ou o cartaz pulsa sozinho, use @keyframes.
a {
color: #0b4f6c;
transition: color 0.2s ease;
}
a:hover,
a:focus-visible {
color: #c45c26;
}
A transição fica no estado inicial. Se você colocar só no :hover, o retorno pisca.
Copie. Mude 0.2s para 2s. Preveja a lentidão. Recarregue. Volte para 0.2s: movimento curto informa, não atrapalha.
Erro comum: transition: all. Propriedades que não devem animar entram na dança. Liste color ou transform.
Transformação
transform move, gira e escala sem quebrar o fluxo tanto quanto top e margin. Funções comuns: translate, rotate, scale. Várias no mesmo valor, separadas por espaço.
.cartao {
transform-origin: center center;
transition: transform 0.2s ease;
}
.cartao:hover,
.cartao:focus-within {
transform: translateY(-0.25rem);
}
:focus-within cobre o cartão com um link dentro, para o teclado ter o mesmo efeito do hover. Não anime só :hover.
transform-origin muda o pivô, como background-position: left top, center bottom.
Neste volume não empilhamos prefixos -webkit- em transform. Os navegadores atuais do material já entendem a propriedade sem prefixo.
Keyframes
Dois pedaços: o nome em @keyframes e a propriedade animation no seletor.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Movimento — Oficina Costeira</title>
<link rel="stylesheet" href="assets/stylesheets/main.css">
</head>
<body>
<p class="selo">Inscrições fictícias</p>
</body>
</html>
@keyframes pulso {
from { transform: scale(1); }
to { transform: scale(1.05); }
}
.selo {
display: inline-block;
animation: pulso 1.2s ease-in-out infinite alternate;
}
from e to são 0% e 100%. Estágios no meio usam porcentagem. infinite repete. alternate vai e volta. Sem alternate, o selo salta do fim para o começo.
Mude infinite para 2. Preveja duas idas. Recarregue.

Prefers-reduced-motion
Algumas pessoas enjoam com movimento. O sistema operacional oferece reduzir animações. O CSS pergunta isso.
@media (prefers-reduced-motion: no-preference) {
a {
transition: color 0.2s ease;
}
.selo {
animation: pulso 1.2s ease-in-out infinite alternate;
}
}
Fora da media query, o selo fica parado e o link ainda muda de cor no instante. Quem pediu menos movimento não espera o pulso.
A forma inversa também vale: anime por padrão e zere dentro de prefers-reduced-motion: reduce. O importante é o movimento não ser obrigatório.
No Windows, a opção aparece nas configurações de acessibilidade de animação. Ative, recarregue, confira o selo parado.
Erro comum: esconder texto com animação que nunca termina. Se a pessoa reduz movimento, o texto precisa já estar visível.
O que não animar
Não anime width e height se transform: scale resolve. Não faça a página inteira deslizar ao rolar. Não dispare som. Não use movimento para informação única: cor e texto também devem dizer o mesmo.
Parallax e carrossel automático saem deste volume. Um cartão que sobe 4 px no foco já ensina transição.
:target pode receber uma transição de fundo, sem deslocar o bloco para fora da tela.
.aviso {
transition: background-color 0.2s ease;
}
.aviso:target {
background-color: #fff3e8;
}
Conferir hover, foco e preferência
Abra o exemplo. Passe o mouse no link: a cor desliza. Tab: o anel de 3 px aparece e a cor também, se você agrupou :focus-visible. Ative reduzir movimento e recarregue: o pulso some, o anel permanece.
Estreite a janela. Nada deve tremer na carga. Se @keyframes no body empurra o layout, tire. Animação decorativa não empurra conteúdo.
animation-play-state: paused no :hover é um truque. Quem pede menos movimento precisa do corte na media query, não só do hover. O exemplo 34-animacoes-css envolve a transição do link em prefers-reduced-motion: no-preference. Copie esse recorte para o pulso do selo.
Duração: 150 ms a 300 ms para cor e foco. Loop infinito só em enfeite que não esconde texto. animation-delay pode esperar; não use delay longo no conteúdo principal, a pessoa acha que a página travou.
Mude só ease para linear na transição do link. Preveja o ritmo. Recarregue.

O que o teclado precisa ver
Movimento no hover sem equivalente no foco exclui quem não usa mouse. Agrupe :hover e :focus-visible quando a mudança for informativa. Se o cartão só “embeleza”, a media query de redução já corta o excesso.
Não anime opacity até 0 num botão que continua clicável. A pessoa não vê o alvo e o Tab ainda para nele. Some de verdade com display só se o conteúdo não for necessário — e mesmo assim o HTML de destino deve existir em outro lugar.
@media (prefers-reduced-motion: reduce) {
.selo {
animation: none;
}
a {
transition: none;
}
}
Este bloco é o inverso do exemplo: corta na preferência reduce. Os dois estilos funcionam. Escolha um e mantenha o arquivo coerente.
Copie o bloco reduce. Ative a opção do sistema. Preveja o selo parado. Recarregue.
Atividades
- Onde se declara a
transition: no estado inicial ou só no:hover? - Qual media query desliga movimento para quem pediu menos animação?
- Por que
:focus-withinno cartão importa? animation: ... infinitesem alternativa de redução causa qual problema?- Cite uma propriedade melhor que
toppara um recuo curto no hover.
Respostas comentadas
- No estado inicial. Assim a ida e a volta animam. Só no
:hovero retorno pisca. @media (prefers-reduced-motion: reduce)para zerar, ou colocar as animações dentro deno-preference.- O teclado foca o link interno. Sem
:focus-within, só o mouse levanta o cartão. - O movimento não para. Quem enjoa ou pede redução continua vendo o loop.
transform: translateY(...), com transição emtransform.
Referências
O arquivo completo
Referência do exemplo 34-animacoes-css, reproduzida aqui para que você possa conferir sem sair da página.
assets/javascript/simulacao.js
const form=document.querySelector("#simulacao");if(form){form.querySelector("button[type=submit]").disabled=false;form.addEventListener("submit",event=>{event.preventDefault();document.querySelector("#retorno").textContent="Simulação concluída. Nada foi enviado ou guardado.";});}