Scroll Snap com CSS na prática

A quantidade de JavaScript que precisamos para criar webapps complexos tem crescido bastante, mas o CSS também está correndo atrás de oferecer cada vez mais funcionalidades para estilização e comportamento visual.

Scroll Snapping (ou pular no scroll) é uma técnica bastante usada na web em componentes ou até sites inteiros para melhorar a experiência dos usuários. Com snap-scroll-type e outras propriedades, conseguimos implementar essa técnica usando apenas CSS.

Índice

O básico de Scroll Snapping com CSS

Sabe aquele site com seções que tomam quase a tela inteira e que pulam o scroll para a próxima seção? Dá para implementar essa funcionalidade com só um pouco de CSS.

<section class="section section-1">Seção 1</section>
<section class="section section-2">Seção 2</section>
<section class="section section-3">Seção 3</section>
<section class="section section-4">Seção 4</section>
<section class="section section-5">Seção 5</section>
<section class="section section-6">Seção 6</section>
<section class="section section-7">Seção 7</section>
<section class="section section-8">Seção 8</section>

Com esse HTML, podemos definir scroll-snap-type: y mandatory no contêiner de scroll (que nesse caso é o próprio elemento html) e definir o lugar onde o scroll irá “grudar” com scroll-snap-align: start.

html {
  scroll-snap-type: y mandatory;
}

/* O Safari exige a definição no body */
body {
  scroll-snap-type: y mandatory;
}

.section {
  scroll-snap-align: start;
  height: 70vh;
}

/* resto do CSS no Codepen */

Link para o Codepen.

A propriedade scroll-snap-type recebe valores que indicam a direção (x, y, both, etc.) e o comportamento (mandatory e proximity). Ao usar scroll-snap-type: y mandatory, nós estamos indicando que queremos que o scroll sempre “pule e grude” com elementos na vertical.

Para definir os pontos de contato com os elementos que estão dentro do contêiner de scroll, usamos scroll-snap-align. Essa propriedade recebe valores como start, end, center e também none.

Mandatory vs Proximity

Os dois tipos de comportamento do scroll-snap-type são mandatory e proximity.

Em mandatory, o navegador sempre tentará “pular e grudar” no elemento mais próximo. Esse é o comportamento que normalmente queremos.

Com proximity, o navegador só irá “pular e grudar” no elemento mais próximo caso ele esteja realmente próximo. A definição de “próximo” varia de navegador para navegador. Apesar de um pouco confuso, esse comportamento pode ser muito útil se o usuário precisa usar o scroll para ver mais conteúdo de uma seção e apenas “pular” para a próxima quando estiver perto dela.

Link para o Codepen.

Scroll snapping na horizontal

Além de grandes seções na vertical, scroll snapping é bem útil em componentes menores, como uma lista horizontal. Com scroll snapping, podemos criar a mesma interação que muitos apps nativos como Netflix e App Store usam em suas listas.

Link para o Codepen.

Neste exemplo, podemos rolar a lista horizontal e o scroll irá “pular e grudar” em uma das caixas, impedindo que a lista fique em uma posição de scroll estranha.

Para isso, basicamente criamos um contêiner de scroll horizontal, usamos scroll-snap-type: x mandatory e definimos o ponto de contato das caixas com scroll-snap-align: start.

<section class="horizontal-section">
  <div class="app-icon"></div>
  <div class="app-icon"></div>
  <div class="app-icon"></div>
  <div class="app-icon"></div>
  <div class="app-icon"></div>
  <div class="app-icon"></div>
  <div class="app-icon"></div>
  <div class="app-icon"></div>
</section>
.horizontal-section {
  overflow-x: scroll;
  scroll-snap-type: x mandatory;
}

.app-icon {
  scroll-snap-align: start;
}

/* resto do estilo no Codepen */

Para evitar que o scroll alinhe na borda do contêiner, adicionamos um scroll-padding-left: 20px. Assim o scroll “gruda” em uma posição que fica alinhada com outros elementos na tela e também nos deixa ver uma parte das outras caixas.

Diagrama de como scroll-padding-left: 20px funciona

A propriedade scroll-padding recebe os mesmo valores que padding. Ela é bem útil para controlar um pouco onde o scroll irá “grudar”, principalmente se você tiver um elemento como um header/menu fixo flutuando no topo da tela.

Scroll Snapping em 2D (vertical e horizontal)

Não é tão comum assim termos scroll vertical e horizontal ao mesmo tempo em um elemento, mas isso não quer dizer que você não possa fazer o snapping em 2D.

Para “pular e grudar” o scroll vertical e horizontal, é só usar scroll-snap-type: both mandatory. Talvez isso seja útil para quando você criar uma grande galeria de fotos.

Link para o Codepen.

Explorando as possibilidades do scroll-snap-align

Além de start que já vimos nos exemplo acima, a propriedade scroll-snap-align também recebe valores como center e end. Isso muda bastante o comportamento.

Para ficar mais fácil de visualizar a diferença entre esses valores, eu criei esse Codepen demonstrando start, center e end lado-a-lado. Nele, temos linhas tracejadas em vermelho mostrando onde o scroll vai tentar “grudar”.

Link para o Codepen.

Um dos valores que eu mais gosto para o scroll-snap-align é center, porque ele deixa você facilmente criar componentes como um carrossel (também conhecido como slider).

Diferenças entre navegadores

O suporte dos navegadores para Scroll Snapping é bastante bom, mais de 91% dos usuários no mundo. Apesar da especificação ser bem suportada, ela ainda é um rascunho e deve ser utilizada para progressivamente melhorar a experiência do usuário. Alguns navegadores implementam apenas parte da especificação atual.

Além disso, a especificação é subjetiva em algumas áreas, dando aos navegadores a discrição de implementar o algoritmo de snapping. Antes de aplicar Scroll Snap em seu projeto em produção, teste em diversos navegadores usados pelos seus usuários.

Continue aprendendo

Vimos apenas uma parte do Scroll Snapping no CSS neste post. Abaixo, separei algumas referências que utilizei para criar este post e seus exemplos:

Bons estudos!