Pular para o conteúdo

Meu site travava ao rolar a página — e não era a hospedagem, era o CSS

por Lucas Almeida 5 min de leitura

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:

  1. 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 fixed com blend ou filtro.
  2. 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-filter ou filter: blur() em elemento position: fixed, animado em loop ou que recebe transform no scroll. backdrop-filter em header fixo de altura pequena é aceitável; em camada de tela cheia, não.
  • Nenhum requestAnimationFrame em loop infinito. Sempre condicionar a “há algo se movendo” e encerrar quando parar.
  • Animar apenas opacity e transform — as duas propriedades que o navegador resolve sem refazer layout nem pintura.
  • will-change só 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.

Lucas Almeida
Redação DigitalRadar

Desenvolvedor e fundador da Elion Estúdio, Lucas Almeida é especialista em tecnologias emergentes, games e análise de mercado. Com experiência em SEO e growth marketing, foca em conteúdo aprofundado que conecta o universo da tecnologia com o dia a dia das pessoas.

Apurado a partir das fontes citadas e verificado antes da publicação. Veja a nossa Política Editorial.

Deixe seu comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *