stamatios
← Voltar ao feed
Cartões translúcidos se sobrepõem com pulso de radar na interseção, detecção de colisão feita apenas com CSS
Dev & Engenharia

CSS ganha forma de detectar elementos sobrepostos sem JavaScript

resumo de ~3 min

O problema: detectar sobreposição sem JavaScript

Ao trabalhar em um site de portfólio, o design engineer Ahmad Shadeed se deparou com uma questão: é possível saber, apenas com CSS, quando um elemento decorativo atinge ou colide com outro? A resposta inicial parecia ser não. O desafio é dinâmico: o elemento pode ter posição ou tamanho fluidos, mudando conforme o viewport ou o container, e o objetivo não é escondê-lo com uma media query, mas escondê-lo justamente ao sobrepor outra seção.

A ideia central é medir o espaço entre o elemento decorativo e a seção e, quando esse espaço ficar menor que um valor definido, ocultar o elemento decorativo. Os exemplos do artigo funcionam apenas em navegadores que suportam animações orientadas a scroll (scroll-driven animations) e anchor positioning.

Medindo o espaço com anchor positioning

O primeiro passo usa CSS anchor positioning. Shadeed cria um elemento chamado measure, posicionado absolutamente e ancorado em duas âncoras: a borda direita do elemento decorativo (marcado com anchor-name: --line) e a borda esquerda da seção (--section). As propriedades left, right, top e bottom do elemento de medição são definidas pelas funções anchor(), de modo que ele passa a ocupar exatamente o espaço entre os dois elementos. Redimensionando o container, é possível ver esse espaço destacado mudar de largura em tempo real.

Restava a questão: como usar esse espaço medido para agir, por exemplo, escondendo o elemento decorativo?

timeline-scope: conectando elementos via animações de scroll

A segunda peça vem das scroll-driven animations com timeline-scope. Em um exemplo inspirado na documentação da MDN, rolar dentro de uma caixa dispara uma animação em outra caixa - como se um elemento fosse conectado ao outro. A caixa da esquerda define scroll-timeline: --box e a da direita usa animation-timeline: --box. Para que a timeline --box fique acessível fora do elemento que a cria, ela precisa ser elevada ao elemento pai com timeline-scope: --box, tornando-a disponível em todo o container da seção.

O truque do elemento de medição com overflow

No elemento de medição, Shadeed adiciona um pseudo-elemento ::after com largura fixa de 20px e define overflow-x: auto no container. Quando o espaço entre os elementos fica menor que 20px, o pseudo-elemento não cabe mais e o measure entra em overflow, tornando-se rolável. Esse overflow é o sinal de colisão.

A ponte com as scroll-driven animations vem de uma observação importante: não é preciso rolar de fato para disparar a animação - basta existir conteúdo suficiente para o container transbordar. Em uma demonstração separada, Shadeed mostra isso invertendo uma variável CSS (--bg-color) via @keyframes em que os valores de from e to são iguais, já que o progresso da animação é irrelevante; o que importa é o momento em que o overflow ocorre.

Aplicando isso ao problema: o elemento measure define uma scroll timeline (--touch no eixo x), o body declara timeline-scope: --touch e uma animação de 1ms inverte a variável --touching de 0 para 1 assim que há overflow. Em seguida, uma style container query no elemento decorativo consulta essa variável:

.decor {
  @container style(--touching: 1) {
    opacity: 0;
  }
}

Com isso, o elemento decorativo desaparece no exato instante em que passaria a sobrepor a seção - tudo em CSS puro.

Conclusão e ressalvas

Shadeed admite que não esperava resolver o problema com CSS. Ele ressalta que a técnica não é suportada em todos os navegadores, mas a considera uma melhoria progressiva (enhancement) para o projeto em que trabalha. Ele também credita a Bramus, cujo trabalho sobre scroll-driven animations serviu de base para a solução. A conclusão prática: às vezes vale ir além do caminho óbvio, porque a solução pode existir dentro do próprio CSS.