Motion UX
Aplique estas regras ao projetar e escrever a interação. Primeiro, inspecione os tokens de motion do projeto: o arquivo AGENTS.md, CLAUDE.md, arquivos de tokens e documentos de design do repositório substituem os valores padrão abaixo.
Núcleo sempre ativo
- Decisão padrão: UI funcional geralmente fica na faixa de 120–180ms. Tokens do projeto têm prioridade.
- Anime para continuidade espacial, mudança de estado, feedback de entrada ou para evitar um aparecimento brusco.
- Prefira transições nomeadas de
transformeopacity. Nunca usetransition: all. - Superfícies que entram devem desacelerar; superfícies que saem devem acelerar. Saídas podem ser mais curtas.
- Superfícies com escala inicial devem começar perto de
scale(0.96), nunca descale(0). - Mantenha o movimento substituível sob
prefers-reduced-motion: reduce; preserve a compreensão e o acesso pelo teclado. - Capture o ponteiro para arrastos, proteja contra toques secundários e teste gestos em hardware real.
- Antes de escrever motion ou tocar em um overlay comum, leia este arquivo por completo. Use
REFERENCE.mdapenas para clip paths, 3D, WAAPI ou mergulhos profundos em performance.
1. Decida se merece motion
Use a frequência de exposição como restrição: ações repetidas constantemente devem ser instantâneas ou quase; superfícies ocasionais podem estabelecer continuidade espacial; momentos raros e explicativos podem ter mais personalidade. O motion deve esclarecer uma mudança, reconhecer uma entrada, preservar a localização ou prevenir um aparecimento brusco. Se não fizer nada disso, remova-o. Não faça um atalho de teclado esperar por motion decorativo.
2. Escolha uma duração
A faixa de 120–180ms é uma decisão padrão: é intencionalmente mais rigorosa que o short3 (150ms), short4 (200ms) e a faixa de transição padrão (250–400ms) publicados no Material 3. Ela expressa uma preferência contra UI funcional lenta, não um requisito de acessibilidade ou plataforma publicado.
| Superfície | Ponto de partida |
|---|---|
| Feedback de pressionamento | 100–140ms |
| Tooltip ou popover pequeno | 120–160ms |
| Menu ou dropdown | 140–180ms |
| Modal | 180ms |
| Folha (sheet) | 180ms; até 250ms quando percorre a maior parte da viewport |
Use o extremo mais curto para saídas. Estes são valores padrão, não substituições para tokens do produto.
3. Escolha uma easing
Uma superfície que entra deve chegar com ênfase que se estabiliza (desaceleração); uma saída deve desaparecer rapidamente (aceleração). O Material 3 nomeia essas direções explicitamente:
:root {
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--ease-enter: cubic-bezier(0.05, 0.7, 0.1, 1); /* desaceleração-enfatizada */
--ease-exit: cubic-bezier(0.3, 0, 0.8, 0.15); /* aceleração-enfatizada */
}
Use linear apenas para progresso temporal. Use uma curva ease-in-out para um estado que percorre um ponto intermediário deliberado. Evite escolher uma curva apenas pelo nome: inspecione a forma plotada e a direção do movimento.
4. Mantenha-o barato para renderizar
Prefira transform e opacity, que podem ficar no compositor. Nomeie as propriedades e mantenha o layout fora da animação. Evite animar top, left, width, height, margem ou padding, a menos que a mudança de layout seja o objetivo e o custo seja aceito. Para altura intrínseca, um wrapper pode fazer a transição de grid-template-rows: 0fr para 1fr, com um wrapper interno min-height: 0 recortado por overflow: hidden. Isso ainda realiza layout; faça o perfil em vez de tratá-lo como um atalho apenas para o compositor. Use will-change tarde, brevemente e somente após o perfil mostrar que ajuda; camadas consomem recursos.
5. Torne os estados corretos
- Dê feedback visual a controles pressionáveis; use um estado de cor, borda ou sombra existente, ou um pequeno
scale(0.98)quando couber. Não sobrescreva uma transformação de posicionamento. - Preserve o foco visível, a ativação pelo teclado e o comportamento desabilitado. A animação não deve atrasar o posicionamento do foco, o tratamento de Escape ou as atualizações de estado semântico.
- Popovers, menus e tooltips crescem a partir de uma âncora via
transform-origin; modais centralizados crescem a partir do centro. Mantenha a origem estável enquanto o estado muda. - Prefira transições para estados que podem ser acionados novamente. Use keyframes para um evento autocontido.
- Restrinja affordances apenas de hover com
@media (hover: hover) and (pointer: fine). - Para um elemento que está entrando, use
@starting-styleonde for suportado ou um estado montado que torne o estilo inicial observável antes da transição.
6. Movimento reduzido significa uma substituição
A configuração prefers-reduced-motion cobre a remoção, redução ou substituição do movimento; panorâmica e escala em grande escala podem ser gatilhos vestibulares. Substitua viagem e escala por mudanças de opacidade e cor onde elas ainda explicam o estado. Mantenha mudanças de foco, carregamento e visibilidade compreensíveis. Não faça um elemento desaparecer apenas porque o usuário pediu menos movimento. Respeite essa preferência em animações controladas por JavaScript, bem como em CSS, incluindo mudanças de preferência enquanto a página está aberta. Não dependa de um evento transitionend para completar uma mudança de estado essencial: a transição pode estar desabilitada ou cancelada. Escopo das substituições para o componente afetado, em vez de forçar globalmente todas as animações para uma duração minúscula.
7. Gestos
Chame setPointerCapture() quando um arrasto começar: eventos de ponteiro subsequentes continuarão a mirar no elemento capturador até a liberação. Dispense com uma regra de velocidade-ou-distância ajustada; não há uma constante universal de flick, então calibre no dispositivo real. Mantenha o movimento limitado com amortecimento em vez de uma borda dura. Ignore pontos de toque adicionais depois que um gesto assumir o ponteiro. Permita ao usuário reverter um gesto em andamento. Trate pointercancel e lostpointercapture limpando o estado de arrasto. Escolha touch-action para o eixo do gesto sem bloquear desnecessariamente a rolagem da página; forneça um botão ou equivalente de teclado.
8. Tratamento de interrupção e molas (springs)
Abertura/fechamento/rea abertura rápidos devem se estabilizar no estado mais recente solicitado. Redirecione a partir do estado visual atual, em vez de reproduzir desde o início. Cancele callbacks, listeners e animações obsoletos na substituição ou desmontagem; uma saída antiga não deve remover uma superfície recém-reaberta. Mantenha conteúdo oculto ou saindo fora da navegação por teclado, de acordo com o ciclo de vida do foco do componente.
Use uma mola quando a manipulação direta ou a continuidade se beneficiam dela, respeitando a biblioteca do projeto. Os parâmetros da mola variam entre engines: não copie valores de stiffness e damping cegamente. Ajuste o tempo de estabilização e o overshoot no componente real; evite bounce em superfícies com muito texto e desabilite viagem sob movimento reduzido.
9. Verifique antes de considerar concluído
Reproduza em velocidade 4× mais lenta e verifique se as propriedades terminam juntas e se originam do lugar certo. Alterne rapidamente para expor snapping. Emule movimento reduzido no DevTools. Exercite um arrasto além do limite, cancele-o, adicione um segundo toque e teste em um dispositivo físico. Para valores de animação calculados em tempo de execução, consulte REFERENCE.md. Indique quais verificações foram realmente realizadas e quais exigem um navegador ou dispositivo físico; a revisão de código sozinha não pode estabelecer suavidade percebida.
Revisar saída
Ao auditar código, relate uma tabela compacta, mais grave primeiro:
| Arquivo:linha | Agora | Mudar para | Por que |
|---|---|---|---|
Menu.tsx:18 | transition: all 300ms | propriedades nomeadas e tokens do projeto | evita animação de layout acidental |
Relate apenas descobertas visíveis no código. Se o diff estiver limpo, diga isso em vez de inventar descobertas.
Curadoria de mad7droid.