Novo Livro!
Categorias
Projetos
CSS

Animando layouts de CSS Grid com CSS Anchor Positioning

Animando layouts de CSS Grid com CSS Anchor Positioning

Quando o espaço disponível muda, um grid comum reposiciona as células de um salto. Dá para fazer essa reorganização acontecer com animação, e com animações interrompíveis, usando CSS Anchor Positioning e transições comuns. Sem JavaScript e sem View Transitions.

No CSS Day, em junho, alguém perguntou ao autor como o Brave (o navegador) teria construído a New Tab Page dele, que exibe alguns tiles dispostos em grid.

O grid em si não tem nada de espetacular. O especial acontece ao redimensionar a viewport: conforme o espaço disponível muda, o grid se reorganiza de forma animada. As animações também são interrompíveis, o que indica que não há View Transitions em jogo.

Como eles fizeram? Não se sabe. Mas, para recriar o efeito, o caminho seria CSS Anchor Positioning com CSS Transitions comuns por cima.

Quem ainda não conhece Anchor Positioning pode começar pelo post Anchors Aweigh!, que explica tudo o que é necessário sobre o recurso (com vídeo).

A técnica

O efeito desejado é este: conforme o grid é redimensionado, as células animam até a nova posição.

Gravação da demonstração do grid animado.

O truque é usar CSS Anchor Positioning e ancorar o conteúdo da célula na própria célula. Como não é possível ancorar conteúdo diretamente numa célula de grid (células de grid são conceitos virtuais), é preciso injetar uma div extra que ocupe o espaço disponível da célula e sirva de âncora.

As transições CSS comuns ficam responsáveis por animar as coisas conforme elas se deslocam.

<div class="grid">
<div class="cell">
<div class="content">CONTENT</div>
</div>
<div class="cell">
<div class="content">CONTENT</div>
</div>
…
</div>
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, 6rem);
gap: 1em;
}
.cell {
height: auto;
aspect-ratio: 1;
anchor-scope: --a;
anchor-name: --a;
}
.content {
position: absolute;
position-anchor: --a;
inset: anchor(inside);
width: 6rem;
height: 6rem;
transition: inset 0.2s ease;
}

São quatro detalhes que fazem o arranjo funcionar:

  • Como nomes de âncora são globais por padrão, eles precisam ficar restritos à subárvore de cada célula. O anchor-scope: --a; cuida disso, permitindo que cada .cell reutilize o identificador --a sem conflitar com as outras.
  • O aspect-ratio: 1; nas .cell garante que elas tenham tanta altura quanto largura.
  • A declaração de transition garante que o inset faça a transição suave de um valor ao outro.
  • As divs .content recebem dimensões explícitas (width e height). Sem isso, elas esticariam e comprimiriam durante a interpolação, conforme o grid é redimensionado.

Ao contrário das View Transitions, esta técnica não exige JavaScript nenhum e as animações são perfeitamente interrompíveis.

Ao redimensionar a viewport rapidamente para frente e para trás, os tiles não entram em fila esperando frames de animação ou promises de transição terminarem: redirecionam na hora, em pleno voo, para a posição recém-calculada.

Uma ressalva: por ora, isso não funciona no Firefox, porque ele não faz interleaving de estilo e layout para Anchor Positioning. Em vez de resolver para valores em pixel interpoláveis, ele mantém o valor de anchor() no computed value time.

Demonstração

Esta é a demonstração construída para mostrar o comportamento:

Animated Reorganizing Grid with Anchor Positioning (No View Transitions!), de Bramus.

Redimensione o embed, ou o .container dentro da própria demonstração, para ver o grid se reorganizar.

Para algo de fidelidade maior (e mais próximo da New Tab Page do Brave):

Animated CSS Grid with CSS Anchor Positioning, de Bramus.
Gravação da demonstração de fidelidade maior.

Se este post pareceu familiar

O autor já havia compartilhado uma demonstração no Bluesky sobre o assunto em junho, e só escreveu o post depois.

Chris Coyier também cobriu a abordagem no Frontend Masters. No texto dele, ele relembra como o Masonry.js animava layouts fluidos e mostra como esse código traz aquele mesmo comportamento nativamente para o CSS: Masonry (with Animation) in CSS.