Rolagem bem controlada com o ajuste de rolagem do CSS

Crie experiências de rolagem bem controladas declarando posições de ajuste de rolagem.

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

O recurso CSS Scroll Snap permite que desenvolvedores da Web criem experiências de rolagem bem controladas declarando posições de ajuste de rolagem. Artigos paginados e carrosséis de imagens são dois exemplos comuns disso. O CSS Scroll Snap oferece uma API fácil de usar e consistente para criar esses padrões de UX conhecidos.

Contexto

O caso do ajuste de rolagem

A rolagem é uma forma popular e natural de interagir com o conteúdo na Web. É a maneira nativa da plataforma de fornecer acesso a mais informações do que as visíveis na tela de uma só vez, sendo especialmente vital em plataformas móveis com espaço de tela limitado. Por isso, não é surpresa que os autores da Web prefiram cada vez mais organizar o conteúdo em listas simples roláveis em vez de hierarquias profundas.

A principal desvantagem da rolagem é a falta de precisão. Raramente uma rolagem termina alinhada a um parágrafo ou frase. Isso é ainda mais evidente em conteúdo paginado ou itemizado com limites significativos quando a rolagem termina no meio da página ou imagem, deixando-a parcialmente visível. Esses casos de uso se beneficiam de uma experiência de rolagem bem controlada.

Os desenvolvedores Web há muito tempo usam soluções baseadas em JavaScript para controlar a rolagem e resolver essa deficiência. No entanto, as soluções baseadas em JavaScript não oferecem uma solução de fidelidade total devido à falta de primitivos de personalização de rolagem ou acesso à rolagem combinada. O CSS Scroll Snap garante uma solução rápida, de alta fidelidade e fácil de usar que funciona de maneira consistente em todos os navegadores.

O CSS Scroll Snap permite que autores da Web marquem cada contêiner de rolagem com limites para operações de rolagem em que terminar. Em seguida, os navegadores escolhem a posição final mais adequada, dependendo das particularidades da operação de rolagem, do layout e da visibilidade do contêiner de rolagem e dos detalhes das posições de ajuste, e fazem uma animação suave até ela. Voltando ao exemplo anterior, quando o usuário termina de rolar o carrossel, a imagem visível se encaixa no lugar. Nenhum ajuste de rolagem necessário pelo JavaScript.

Exemplo de uso do ajuste de rolagem CSS com um carrossel de imagens.
Exemplo de uso do ajuste de rolagem CSS com um carrossel de imagens. Aqui, o ajuste de rolagem garante que, ao final da rolagem, um centro horizontal da imagem seja alinhado com o centro horizontal do contêiner de rolagem.

CSS Scroll Snap

O ajuste de rolagem é o ato de ajustar o deslocamento de rolagem de um contêiner de rolagem para estar em uma posição de ajuste preferida quando a operação de rolagem termina.

Um contêiner de rolagem pode ativar o ajuste de rolagem usando a propriedade scroll-snap-type. Isso informa ao navegador que ele precisa considerar o ajuste desse contêiner de rolagem às posições de ajuste produzidas pelos descendentes. scroll-snap-type determina o eixo em que a rolagem ocorre: x, y ou both, e a rigidez do ajuste: mandatory, proximity. Vamos falar mais sobre elas depois.

Uma posição de ajuste pode ser produzida declarando um alinhamento desejado em um elemento. Essa posição é o deslocamento de rolagem em que o contêiner de rolagem ancestral mais próximo e o elemento são alinhados conforme especificado para o eixo determinado. Os seguintes alinhamentos são possíveis em cada eixo: start, end, center.

Um alinhamento start significa que a borda inicial do ponto de ajuste do contêiner de rolagem precisa estar nivelada com a borda inicial da área de ajuste do elemento. Da mesma forma, os alinhamentos end e center significam que a borda ou o centro final do ajuste do contêiner de rolagem precisam estar alinhados com a borda ou o centro final da área de ajuste do elemento.

Exemplo de vários alinhamentos no eixo de rolagem horizontal.

Os exemplos a seguir ilustram como usar esses conceitos.

Um caso de uso comum para o ajuste de rolagem é um carrossel de imagens. Por exemplo, para criar um carrossel de imagens horizontal que se encaixe em cada imagem à medida que você rola a tela, podemos especificar que o contêiner de rolagem tenha um scroll-snap-type obrigatório no eixo horizontal. Defina cada imagem como scroll-snap-align: center para garantir que o encaixe centralize a imagem no carrossel.

#gallery {
  scroll-snap-type: x mandatory;
  overflow-x: scroll;
  display: flex;
}

#gallery img {
   scroll-snap-align: center;
}
<div id="gallery">
  <img src="cat.jpg">
  <img src="dog.jpg">
  <img src="another_cute_animal.jpg">
</div>

Como as posições de ajuste são associadas a um elemento, o algoritmo de ajuste pode ser inteligente sobre quando e como ajustar, considerando o elemento e o tamanho do contêiner de rolagem. Por exemplo, considere o caso em que uma imagem é maior que o carrossel. Um algoritmo de ajuste ingênuo pode impedir que o usuário mova a tela para ver a imagem completa. No entanto, a especificação exige que as implementações detectem esse caso e permitam que o usuário role livremente pela imagem, encaixando-a apenas nas bordas.

Exemplo: uma página de produto com jornada

Outro caso comum que pode se beneficiar do ajuste de rolagem são páginas com várias seções lógicas para rolar verticalmente, por exemplo, uma página de produto típica. scroll-snap-type: y proximity; é mais adequado para casos como esse. Ele não interfere quando um usuário rola até o meio de uma seção específica, mas também se ajusta e chama a atenção para uma nova seção quando ele rola perto o suficiente.

Para isso, faça o seguinte:

article {
  scroll-snap-type: y proximity;
  /* Reserve space for header plus some extra space for sneak peeking. */
  scroll-padding-top: 15vh;
  overflow-y: scroll;
}
section {
  /* Snap align start. */
  scroll-snap-align: start;
}
header {
  position: fixed;
  height: 10vh;
}
<article>
  <header> Header </header>
  <section> Section One </section>
  <section> Section Two </section>
  <section> Section Three </section>
</article>

Padding e margem de rolagem

A página do produto tem um cabeçalho fixo na parte superior. O design também pediu que parte da seção superior permanecesse visível quando o contêiner de rolagem fosse ajustado para fornecer uma dica de design aos usuários sobre o conteúdo acima.

A propriedade scroll-padding é uma nova propriedade CSS que pode ser usada para ajustar a região visível efetiva do contêiner de rolagem ou snapport, que é usado ao calcular os alinhamentos de ajuste de rolagem. A propriedade define um encarte em relação à caixa de padding do contêiner de rolagem. No exemplo, um encarte adicional 15vh foi adicionado à parte de cima, o que instrui o navegador a considerar uma posição mais baixa, 15vh abaixo da borda superior do contêiner de rolagem, como a borda inicial vertical para o ajuste de rolagem. Ao ajustar, a borda inicial do elemento de ajuste ficará alinhada com essa nova posição, deixando espaço acima.

A propriedade scroll-margin define a quantidade de início usada para ajustar a caixa efetiva do destino de ajuste, semelhante a como scroll-padding funciona no contêiner de rolagem de ajuste.

Talvez você tenha percebido que essas duas propriedades não têm a palavra "snap". Isso é intencional, já que eles modificam a caixa para todas as operações de rolagem relevantes e não apenas o ajuste de rolagem. Por exemplo, o Chrome os considera ao calcular o tamanho da página para operações de rolagem de paginação, como PageDown e PageUp, e também ao calcular o valor da rolagem para a operação Element.scrollIntoView().

Interação com outras APIs de rolagem

API DOM Scrolling

O ajuste de rolagem acontece depois de todas as operações de rolagem, incluindo as iniciadas por script. Ao usar APIs como Element.scrollTo, o navegador calcula a posição de rolagem pretendida da operação e aplica a lógica de ajuste adequada para encontrar o local ajustado final. Portanto, não é necessário que o script do usuário faça cálculos manuais para o ajuste.

Rolagem suave

A rolagem suave controla o comportamento de uma operação de rolagem programática, enquanto o ajuste de rolagem determina o destino dela. Como controlam aspectos ortogonais da rolagem, eles podem ser usados juntos e se complementam.

Comportamento de rolagem esticada

A API de comportamento de rolagem excessiva controla como a rolagem é encadeada em vários elementos e não é afetada pelo ajuste de rolagem.

Observações e práticas recomendadas

Evite usar o ajuste obrigatório quando os elementos de destino estiverem muito espaçados. Isso pode fazer com que o conteúdo entre as posições de ajuste fique inacessível.

Em muitos casos, o ajuste de rolagem pode ser adicionado como um aprimoramento sem precisar detectar recursos. Se necessário, use @supports ou CSS.supports para detectar suporte ao CSS Scroll Snap. Evite usar scroll-snap-type, que também está presente na especificação descontinuada.

Detecção de recursos em CSS

@supports (scroll-snap-align: start) {
  article {
    scroll-snap-type: y proximity;
    scroll-padding-top: 15vh;
    overflow-y: scroll;
  }
}

Detecção de recursos em JavaScript

if (CSS.supports('scroll-snap-align: start')) {
  // use css scroll snap
} else {
  // use fallback
}

Não suponha que APIs de rolagem programática, como Element.scrollTo, sempre terminem no deslocamento de rolagem solicitado. O ajuste de rolagem pode ajustar o deslocamento de rolagem depois que a rolagem programática é concluída. Essa não era uma boa proposição mesmo antes do ajuste de rolagem, já que a rolagem pode ter sido interrompida por outros motivos, mas é especialmente o caso com o ajuste de rolagem.

Próximos lançamentos

A experiência de rolagem foi o foco de uma pesquisa recente da equipe do Chrome. Os resultados da pesquisa identificaram várias áreas que precisam de mais trabalho para diminuir a diferença entre bibliotecas de plug-ins e CSS. O trabalho futuro vai se concentrar em scroll-snap, incluindo:

  1. Disponibilidade e compatibilidade da API em navegadores.
  2. Trabalhar em novas APIs CSS, como scroll-start.
  3. Trabalhe em novos eventos JS, como snapChanged().