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
| Token | Valor | Onde roda |
|---|---|---|
| ease-vidro | cubic-bezier(.22, 1, .36, 1) | Padrão universal — desacelera como vidro assentando. entra-subindo, entra-diagonal, vidro-reflexo |
| ease-facho | cubic-bezier(.4, 0, .2, 1) | Só o facho de luz: acelera e desacelera simétrico, atravessa a tela sem “assentar” |
| toque | 200 ms | Botões de conversão: cor + active:scale(.98) |
| troca | 500 ms | Crossfade do título rotativo (ease-out) |
| brilho | 640 ms | Varredura de luz no hover do botão |
| entrada | 700 ms | entra-subindo e Reveal (fade + subir) |
| cena | 800 ms | entra-diagonal — a faixa da marca abrindo |
| contagem | 1200 ms | Contador (ease-out cúbico, 60 passos) |
| facho | 1400 ms + 500 ms de espera | O reflexo atravessando o hero, uma única vez |
| cue | 1,8 s × 3 | Seta de rolagem — para sozinha depois de 3 ciclos |
| trilho | 28 s linear infinite | Marquee 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.
| Classe | Spec exata | Uso |
|---|---|---|
| .entra-subindo | 700 ms · ease-vidro · both · opacity 0→1 + translateY(36px→0) | Mascote e título do hero. É a entrada padrão da marca |
| .entra-diagonal | 800 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.
| Classe | Spec exata | Uso |
|---|---|---|
| .facho-vidro | 1400 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 |
| .brilho | 640 ms · ease · varredura linear-gradient(105deg) branco 38% · translateX(−130%→130%) | Hover e focus-visible dos botões de conversão |
| .vidro-reflexo | 700 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
| Classe | Spec exata | Regra |
|---|---|---|
| .marquee-trilho | 28 s · linear · infinite · translateX(0→−50%) · will-change: transform | O 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-cue | 1,8 s · ease-in-out · 3 repetições · translateY(0→7px→0) + opacity .7→1→.7 | Convida 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.
| Componente | Comportamento | Cuidado que já está resolvido |
|---|---|---|
| Reveal | IntersectionObserver · fade + translateY(20px) · 700 ms ease-out · prop atraso vira transition-delay | SSR-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 |
| ParallaxY | translateY(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 |
| SegueMouse | translate(x, y) seguindo o mouse · intensidade padrão 10 px (eixo Y a 60% disso) · rAF + listener passive | Desktop only: (pointer: coarse) desliga no celular — no toque não existe hover, e o efeito só gastaria bateria |
| Contador | Anima ao entrar na tela · 1200 ms · ease-out cúbico (1−(1−t)³) · sobe os últimos 60 · tabular-nums | Roda uma vez só (desconecta o observer). Sem JS ou com reduced motion, mostra o número final direto — o dado nunca some |
| TituloRotativo | Alterna 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
| Comportamento | Especificação |
|---|---|
| Idle | Respiração sutil (ciclo ~3 s) + piscar natural a cada 4–6 s. Ele nunca fica estátua |
| Olhar | Segue o que apresenta; volta pra “câmera” ao concluir |
| Gestos | Mãos explicam em arcos suaves na altura do peito; joinha sobe com ease-vidro |
| Entrada | Caminha ou desliza de uma lateral (500–800 ms), assenta com leve acomodação |
| Saída | Acena e sai pelo mesmo lado — nunca some em corte seco |
| Recorte | O contorno branco do sticker acompanha o mascote em movimento — é a assinatura da marca, não some no meio da animação |
| Timing geral | Calmo e confiante: sem hiperatividade, sem bounce elástico |
Receitas por formato
| Formato | Estrutura |
|---|---|
| Reels / TikTok | Abertura 0,8 s (logo + facho) → conteúdo com lower thirds → encerramento 1,5 s (assinatura + CTA “Chama no zap”) |
| Stories | Cartão sobe com o texto-chave; mascote entra na segunda tela; CTA fixo no rodapé |
| Anúncio 15 s | Problema (2 s, discreto) → solução com vidro brilhando (8 s) → oferta + facho (5 s) |
| Institucional | Ritmo calmo, cortes ~800 ms, trilha leve; fecha com a assinatura + facho |
| Apresentações | Entradas 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