Dinâmico

Motion

O movimento da marca é vidro deslizando: suave, preciso, sem quique de borracha. As specs abaixo não são desejo — são o que o site roda hoje. Fonte da verdade: components/site-alemao/shell.tsx (bloco de estilo), ui.tsx (componentes) e globals.css (sistema de vidro). Onde este capítulo divergir do site, o site está certo.

Curvas e durações reais

TokenValorOnde roda
ease-vidrocubic-bezier(.22, 1, .36, 1)Padrão universal — desacelera como vidro assentando. entra-subindo, entra-diagonal, vidro-reflexo
ease-fachocubic-bezier(.4, 0, .2, 1)Só o facho de luz: acelera e desacelera simétrico, atravessa a tela sem “assentar”
toque200 msBotões de conversão: cor + active:scale(.98)
troca500 msCrossfade do título rotativo (ease-out)
brilho640 msVarredura de luz no hover do botão
entrada700 msentra-subindo e Reveal (fade + subir)
cena800 msentra-diagonal — a faixa da marca abrindo
contagem1200 msContador (ease-out cúbico, 60 passos)
facho1400 ms + 500 ms de esperaO reflexo atravessando o hero, uma única vez
cue1,8 s × 3Seta de rolagem — para sozinha depois de 3 ciclos
trilho28 s linear infiniteMarquee dos serviços

Só existem duas curvas na marca. O padrão é ease-vidro; a exceção é o facho. Qualquer terceira curva inventada (bounce, elástico, spring) está fora.

Entradas de cena

As duas animações que abrem o hero e cada bloco do site.

ClasseSpec exataUso
.entra-subindo700 ms · ease-vidro · both · opacity 0→1 + translateY(36px→0)Mascote e título do hero. É a entrada padrão da marca
.entra-diagonal800 ms · ease-vidro · both · transform-origin: top · skewX(−14deg) mantido, scaleY(0→1)A faixa diagonal do hero se desdobrando de cima pra baixo
@keyframes alemao-subir {
  from { opacity: 0; transform: translateY(36px); }
  to   { opacity: 1; transform: translateY(0); }
}
.entra-subindo { animation: alemao-subir 700ms cubic-bezier(.22,1,.36,1) both; }

@keyframes alemao-diagonal {
  from { transform: skewX(-14deg) scaleY(0); }
  to   { transform: skewX(-14deg) scaleY(1); }
}
.entra-diagonal {
  transform-origin: top;
  animation: alemao-diagonal 800ms cubic-bezier(.22,1,.36,1) both;
}

O ângulo da marca é −14°. O skew negativo é o mesmo do grafismo do hero (-skew-x-[14deg]). Não arredonde pra −15°, não espelhe pra +14°: o topo sempre pende pra direita e a base pra esquerda — a faixa lê como uma barra “/”, a mesma inclinação do A da marca. O mesmo ângulo, em toda peça.

A assinatura: o reflexo de vidro

O sol escorregando num para-brisa limpo — o grafismo da marca virado movimento.

ClasseSpec exataUso
.facho-vidro1400 ms · atraso 500 ms · cubic-bezier(.4,0,.2,1) · both · translateX(−70vw→120vw) skewX(−18deg)Atravessa o hero UMA vez, depois da página assentar. Nunca em loop
.brilho640 ms · ease · varredura linear-gradient(105deg) branco 38% · translateX(−130%→130%)Hover e focus-visible dos botões de conversão
.vidro-reflexo700 ms · ease-vidro · varredura linear-gradient(105deg) branco 32% · translateX(−130%→130%)Hover/focus-within de card de vidro. O irmão calmo do .brilho
@keyframes alemao-facho {
  from { transform: translateX(-70vw) skewX(-18deg); }
  to   { transform: translateX(120vw) skewX(-18deg); }
}
.facho-vidro { animation: alemao-facho 1400ms 500ms cubic-bezier(.4,0,.2,1) both; }

Dois ângulos, dois papéis — não confunda. O grafismo estrutural (faixa, recorte, diagonal) é −14°. Já a luz que passa por cima — facho e varredura — vai mais deitada: −18° no facho, 105° no gradiente do brilho. Luz nunca é paralela à estrutura; é esse desencontro que faz parecer reflexo de verdade.

Vidro só sobre fundo rico. .vidro-reflexo é do sistema .vidro-* e só aparece sobre azul, gradiente, foto ou padrão. Sobre branco ou gelo, card sólido — sem reflexo.

Movimento contínuo e ambiente

ClasseSpec exataRegra
.marquee-trilho28 s · linear · infinite · translateX(0→−50%) · will-change: transformO conteúdo é duplicado (2 blocos), por isso −50% fecha o loop sem salto. Pausa em .marquee:hover e :focus-within — leitura e teclado sempre vencem o movimento
.scroll-cue1,8 s · ease-in-out · 3 repetições · translateY(0→7px→0) + opacity .7→1→.7Convida a rolar e cala a boca. Nunca infinite

Acessibilidade do marquee: o segundo bloco duplicado é aria-hidden — o leitor de tela lê a lista de serviços uma vez só, não duas.

Componentes de movimento (JS)

Em components/site-alemao/ui.tsx. Todos checam prefers-reduced-motion antes de mexer em qualquer coisa.

ComponenteComportamentoCuidado que já está resolvido
RevealIntersectionObserver · fade + translateY(20px) · 700 ms ease-out · prop atraso vira transition-delaySSR-safe: sem JS o conteúdo nasce VISÍVEL. Bloco já na tela nunca é escondido. rootMargin com teto gigante (100000px) faz bloco que passou pra cima contar como visto — sem isso, um salto de âncora deixava seções invisíveis pra trás
ParallaxYtranslateY(scrollY × fator), fator padrão 0.12 · atualiza dentro de requestAnimationFrame · listener passiveÉ decorativo: sai da árvore de acessibilidade com aria-hidden. Reduced motion não registra o listener
SegueMousetranslate(x, y) seguindo o mouse · intensidade padrão 10 px (eixo Y a 60% disso) · rAF + listener passiveDesktop only: (pointer: coarse) desliga no celular — no toque não existe hover, e o efeito só gastaria bateria
ContadorAnima ao entrar na tela · 1200 ms · ease-out cúbico (1−(1−t)³) · sobe os últimos 60 · tabular-numsRoda uma vez só (desconecta o observer). Sem JS ou com reduced motion, mostra o número final direto — o dado nunca some
TituloRotativoAlterna a cada 3600 ms · crossfade 500 ms ease-out + translateY(−12px) na saída · grid 1×1 empilhadoÉ aria-hidden e anda com um irmão sr-only fixo: leitor de tela e SEO leem um título estável em vez de texto que troca sozinho

Reduced motion — a regra transversal

Não é opcional nem “nice to have”: quem pede menos movimento vê a página parada, inteira e legível.

@media (prefers-reduced-motion: reduce) {
  .marquee-trilho { animation: none; transform: none; }
  .entra-subindo, .entra-diagonal, .facho-vidro, .scroll-cue { animation: none; }
  .facho-vidro { display: none; }
  .brilho::after { display: none; }
  .vidro-reflexo::after { display: none; }
}

Parar não é esconder. Repare que animation: none com both deixa o elemento no estado final (visível), não no inicial. Só o facho some de vez — display: none — porque ele é 100% enfeite e não carrega conteúdo nenhum. Componente JS segue a mesma lei: matchMedia("(prefers-reduced-motion: reduce)") logo na primeira linha do efeito, mais motion-reduce:transition-none nas transições de classe.

Sistema de animação do mascote

ComportamentoEspecificação
IdleRespiração sutil (ciclo ~3 s) + piscar natural a cada 4–6 s. Ele nunca fica estátua
OlharSegue o que apresenta; volta pra “câmera” ao concluir
GestosMãos explicam em arcos suaves na altura do peito; joinha sobe com ease-vidro
EntradaCaminha ou desliza de uma lateral (500–800 ms), assenta com leve acomodação
SaídaAcena e sai pelo mesmo lado — nunca some em corte seco
RecorteO contorno branco do sticker acompanha o mascote em movimento — é a assinatura da marca, não some no meio da animação
Timing geralCalmo e confiante: sem hiperatividade, sem bounce elástico

Receitas por formato

FormatoEstrutura
Reels / TikTokAbertura 0,8 s (logo + facho) → conteúdo com lower thirds → encerramento 1,5 s (assinatura + CTA “Chama no zap”)
StoriesCartão sobe com o texto-chave; mascote entra na segunda tela; CTA fixo no rodapé
Anúncio 15 sProblema (2 s, discreto) → solução com vidro brilhando (8 s) → oferta + facho (5 s)
InstitucionalRitmo calmo, cortes ~800 ms, trilha leve; fecha com a assinatura + facho
ApresentaçõesEntradas em fade+rise 700 ms com ease-vidro; um elemento por vez; sem transição giratória

Fica de olho

O que mantém a marca inteira — e o que a quebra.

Sempre

  • ease-vidro cubic-bezier(.22,1,.36,1) como padrão de tudo
  • Diagonal da marca sempre em −14°; a luz por cima, mais deitada (−18° / 105°)
  • Facho de luz uma única vez por peça, nunca em loop
  • Movimento contínuo pausa no hover e no focus-within
  • Todo componente checa prefers-reduced-motion antes de animar
  • Texto que troca sozinho tem irmão sr-only estável

Nunca

  • Bounce elástico, spin, zoom dramático ou uma terceira curva inventada
  • Diagonal em 22°, +14° ou espelhada (o site manda: −14°)
  • Marquee que não pausa, ou scroll-cue em infinite
  • Reveal que esconde conteúdo above-the-fold ou some sem JS
  • Vidro e reflexo sobre fundo branco ou gelo
  • Animação que não pode ser desligada