Meu site travava ao rolar a página — e não era a hospedagem, era o CSS
O DigitalRadar abria rápido. O tempo até o primeiro conteúdo aparecer era bom, o servidor respondia bem, as imagens estavam todas em WebP. E mesmo assim a experiência era ruim: ao rolar a página, a rolagem engasgava; o cursor personalizado se arrastava atrás do mouse; passar o mouse sobre os cards dava a sensação de um site pesado.
Minha primeira suspeita foi a hospedagem. A segunda foi o WordPress. Nenhuma das duas tinha a ver com o problema — ele estava inteiro no navegador, em quatro decisões de CSS e uma de JavaScript.
Por que medir “velocidade” não encontra esse problema
As ferramentas que a gente roda por reflexo — PageSpeed, GTmetrix — medem principalmente o carregamento: quanto tempo até a página aparecer e ficar utilizável. Elas são ótimas nisso e eram justamente onde o meu site ia bem.
O que eu tinha era um problema de runtime: o custo de desenhar cada quadro depois que a página já carregou. Isso não aparece numa nota de 0 a 100. Aparece no painel Performance do DevTools, quando você grava alguns segundos rolando a página e vê a barra verde de rendering estourando o orçamento de 16 milissegundos por quadro.
Os quatro culpados
1. Um overlay de “grão” na tela inteira com mix-blend-mode
O tema tinha uma textura sutil cobrindo o site todo, para dar aspecto de filme:
.dr-body::before {
position: fixed;
inset: 0;
mix-blend-mode: soft-light; /* ← o problema */
}
Esse é o pior dos quatro. Um elemento fixed ocupando a tela inteira com mix-blend-mode obriga o navegador a recompor a página inteira a cada quadro, porque o resultado do blend depende de tudo o que está embaixo — e o que está embaixo muda a cada pixel de rolagem. Removi a camada e metade do engasgo sumiu.
2. Cursor personalizado com blend e loop infinito
O brilho que seguia o mouse tinha dois defeitos somados: usava mix-blend-mode e rodava um requestAnimationFrame permanente, recalculando posição mesmo com o mouse parado.
A correção foi tirar o blend, trocar o posicionamento por translate3d, isolar o elemento com contain e — o principal — fazer o loop parar sozinho. A interpolação roda enquanto a distância entre o cursor e o alvo é relevante; abaixo de meio pixel, o loop encerra:
if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) {
rodando = false;
return; // nada se movendo = nada a desenhar
}
requestAnimationFrame(passo);
3. filter: blur() em áreas grandes
Havia brilhos decorativos com filter: blur(30px) a blur(40px) no hero, no CTA e na newsletter. Desfoque é caro: o navegador precisa renderizar a camada e depois processá-la.
Troquei todos por gradientes radiais, que já nascem suaves e não custam nada:
/* antes */
background: var(--dr-neon); filter: blur(40px);
/* depois */
background: radial-gradient(circle, rgba(0,180,255,.35), transparent 70%);
Tinha até um filter: blur(.3px) num elemento animado — um décimo de pixel de desfoque, invisível na prática, forçando o custo de filtro em todo quadro.
4. Blend em um elemento animado
O mesmo erro do item 1, em escala menor: uma linha de varredura animada usando mix-blend-mode. Saiu o blend, ficou a animação.
Como achar isso no seu site
Dois caminhos no DevTools do Chrome, ambos em menos de um minuto:
- Rendering → Paint flashing. Abra o menu de três pontinhos → More tools → Rendering e marque “Paint flashing”. Role a página. As áreas que piscam em verde estão sendo repintadas. Se a tela inteira pisca ao rolar, você tem um problema de camada — provavelmente um elemento
fixedcom blend ou filtro. - Performance → gravar 5 segundos rolando. Olhe a faixa de Rendering e Painting. Barras longas e repetidas indicam custo por quadro, não de carregamento.
Para caçar os suspeitos direto no código, uma busca no CSS já entrega quase tudo:
grep -rn "mix-blend-mode|backdrop-filter|filter: *blur" assets/css/
As regras que adotei
- Nunca usar
mix-blend-mode,backdrop-filteroufilter: blur()em elementoposition: fixed, animado em loop ou que recebetransformno scroll.backdrop-filterem header fixo de altura pequena é aceitável; em camada de tela cheia, não. - Nenhum
requestAnimationFrameem loop infinito. Sempre condicionar a “há algo se movendo” e encerrar quando parar. - Animar apenas
opacityetransform— as duas propriedades que o navegador resolve sem refazer layout nem pintura. will-changesó durante a animação. Deixar fixo no CSS reserva memória de GPU o tempo todo.- Testar no Performance antes de adicionar efeito novo, não depois de o site ficar lento.
O resultado prático: a rolagem ficou limpa e o cursor passou a acompanhar o mouse. Nenhuma linha de PHP mudou, nenhum plano de hospedagem foi trocado.
Perguntas frequentes
Por que o PageSpeed não acusou esse problema?
Porque ele mede principalmente o carregamento. Travamento ao rolar é custo por quadro depois de a página já estar carregada, e isso aparece no painel Performance do DevTools, não na nota.
mix-blend-mode é sempre ruim?
Não. O problema é a combinação: blend em elemento de tela cheia, fixo ou animado. Num ícone pequeno e estático, o custo é irrelevante.
Como sei se o problema é meu site ou o computador do visitante?
Use a limitação de CPU no DevTools (Performance → CPU → 4x ou 6x slowdown). Se travar com a CPU limitada, vai travar em celular intermediário, que é a maioria do tráfego.
Trocar de hospedagem resolveria?
Não nesse caso. Servidor mais rápido entrega o HTML antes, mas quem desenha cada quadro é o navegador do visitante. Problema de compositing viaja junto com o CSS.
No DigitalRadar, contamos o que quebrou no nosso próprio site e como foi resolvido. Fique no radar.
Apurado a partir das fontes citadas e verificado antes da publicação. Veja a nossa Política Editorial.