Polimento de UI
O polimento vem de uma pilha de pequenos detalhes que se acumulam. Este skill é a referência para saber quais valem a pena ter e quais valores eles assumem.
Ao revisar, diminua a velocidade da interface. O que parece estranho a 10% da velocidade é o que está sutilmente errado em velocidade total.
Mantenha a biblioteca de componentes, tokens e densidade do projeto, e corresponda à sua linguagem de movimento, exceto onde uma regra abaixo prescreve uma interação exata.
Cada duração, curva, escala e desfoque abaixo é um valor específico, não um intervalo para aproximar. cubic-bezier(0.2, 0, 0, 1) não é cubic-bezier(0.4, 0, 0.2, 1), e 0.96 não é 0.95. Use o que está escrito.
Quebra de texto, renderização de fontes, números tabulares e espaçamento de texto pertencem a better-typography. Áreas de toque, foco, suporte a teclado, ARIA e movimento reduzido pertencem a better-accessibility. Agrupamento, espaçamento de seções, breakpoints e RTL espacial pertencem a better-layout.
Raio de borda concêntrico
Raio externo = raio interno + padding. Raios incompatíveis em elementos aninhados é a coisa mais comum que faz uma interface parecer estranha. Receitas de raio, sombra e contorno estão em surfaces.md.
Alinhamento óptico sobre geométrico
Quando o centralização geométrica parece errada, alinhe opticamente. Botões com ícones, triângulos de reprodução e ícones assimétricos precisam de um ajuste manual.
Sombras para elevação, bordas para estrutura
Onde uma borda existe apenas para criar profundidade, prefira valores de box-shadow transparentes em camadas. Mantenha bordas que comuniquem estrutura ou estado: divisores, separadores e estados selecionados ou em foco.
Animações interrompíveis
Use transições CSS para mudanças de estado interativas, porque elas podem ser interrompidas no meio da animação. Reserve keyframes para sequências encenadas que são executadas uma vez.
Dividir e escalonar animações de entrada
Para uma entrada encenada pouco frequente onde a sequência comunica hierarquia, divida o conteúdo em blocos semânticos e os escalone em ~100ms. Animando um contêiner, você obtém menos pelo mesmo custo. Deixe interações de alta frequência sem escalonamento. Veja enter-exit.md.
Animações de saída sutis
Use um translateY fixo pequeno em vez de altura total. As saídas devem ser mais suaves que as entradas. Use ease-out para ambas as direções.
Animações de ícones contextuais
Anime ícones com opacity, scale e blur em vez de alternar a visibilidade. Use exatamente esses valores: scale 0.25 para 1, opacity 0 para 1, blur 4px para 0px.
Com uma biblioteca de movimento (motion ou framer-motion em package.json), corresponda ao caminho de importação dessa biblioteca, ou importações próximas onde ambos existam. Use transition: { type: "spring", duration: 0.3, bounce: 0 }. Bounce é sempre 0.
Sem uma, mantenha ambos os ícones no DOM com um posicionado absolutamente, e faça um cross-fade com cubic-bezier(0.2, 0, 0, 1). Isso lhe dá entrada e saída sem dependência. Ambas as receitas estão em icon-transitions.md.
Contornos de imagem
Dê às imagens um contorno de 1px com baixa opacidade para profundidade consistente. Preto puro no modo claro (oklch(0 0 0 / 0.1)), branco puro no escuro (oklch(1 0 0 / 0.1)). Nunca um preto aproximado como slate ou zinc e nunca um neutro matizado. Um contorno matizado capta a superfície subjacente e parece sujeira na borda da imagem.
Escala ao pressionar
Um scale(0.96) ao clicar dá feedback tátil a um botão. Sempre 0.96; qualquer coisa abaixo de 0.95 parece exagerada. Adicione uma prop static para desligá-lo onde o movimento distrairia. Veja receitas para CSS, Tailwind e Motion.
Pular animação no carregamento da página
Use initial={false} no AnimatePresence para manter as animações de entrada fora da primeira renderização. Verifique se isso deixa as entradas intencionais da página intactas.
Suprimir transições na troca de tema
Uma troca de tema muda cor, fundo, borda e sombra em quase todos os elementos de uma só vez. Cada transição nessas propriedades dispara junto e a troca borra em vez de estalar. Injete *,*::before,*::after{transition:none !important}, force um reflow, depois remova no próximo frame. Veja a receita.
Transicionar apenas o que muda
Sempre nomeie as propriedades exatas: transition-property: scale, opacity. O transition-transform do Tailwind cobre transform, translate, scale, rotate.
Usar will-change com moderação
Apenas para transform, opacity e filter, que a GPU pode compor. Nunca will-change: all. Adicione quando você vir gagueira no primeiro frame, não antes. Veja performance.md.
Combinar traço do ícone com peso do texto
Um ícone próximo ao texto carrega o peso óptico do texto: traço de 1.5px ao lado de texto regular (400), 2px ao lado de semibold (600). Um peso de traço por conjunto de ícones e uma biblioteca de ícones por superfície. Dimensionamento e inversão RTL estão em icons.md.
Um SVG, recolorido por estado
Os ícones usam currentColor e recebem estados de hover, selecionado e desabilitado da cor e opacidade do CSS, nunca de assets separados. Contorno é a variante padrão; preenchimento marca o estado ativo.
Restrição de movimento
Dê feedback instantâneo a interações de alta frequência, ou uma transição de 150ms ou menos em opacidade e cor. Uma animação personalizada ali cobra seu custo de atenção a cada gatilho.
Cada mudança de estado animada também precisa de uma pista estática: cor, um ícone ou um rótulo. Movimento nunca é o único canal de feedback.
Antes de finalizar
| Erro | Correção |
|---|---|
| Ícones parecem descentralizados | Ajuste opticamente com padding, ou corrija o SVG |
| Entrada ou saída encenada brusca | Escalone entradas pouco frequentes; mantenha saídas sutis |
| Troca de tema faz crossfade na página inteira | Desabilite transições para a troca, force um reflow, restaure no próximo frame |
transition: all em elementos | Especifique propriedades exatas |
| Gagueira de animação no primeiro frame | Adicione will-change: transform (com moderação) |
| Ícone fino ao lado de texto em negrito | Corresponda a largura do traço ao peso do texto |
Relatório
Severidade. HIGH quebra uma interação, torna o movimento inutilizável ou deixa uma mudança de estado visível apenas enquanto a animação é executada. MEDIUM é uma inconsistência visível em superfícies, ícones ou movimento. LOW é polimento isolado.
Verificação. Sem um navegador: cada estado que o componente define, significando hover, focus, active, loading e empty, além de durações de movimento e easings lidos do código. Com um: percorra cada estado e reproduza o movimento a 10% da velocidade no painel Animations do navegador. Relate cada verificação que você não pôde executar como Not verified.
Formato. Agrupe as descobertas sob o princípio que cada uma viola, ordenado por severidade, uma linha por causa raiz listando cada localização em que aparece:
| Severidade | Localização | Antes | Depois | Porquê |
|---|
Localização é caminho/para/arquivo:linha. Porquê nomeia o princípio e o impacto para o usuário.
Termine com Block quando qualquer HIGH permanecer, Approve caso contrário, deixando o restante na tabela como trabalho a fazer. Nunca Approve cobertura que você não inspecionou. Sem nada a relatar, declare "Nenhuma descoberta acionável de polimento de UI" e relate a verificação.