Como consultas de contêiner, mas para consultas travadas, fixadas e com transbordamento.
Publicado em 15 de janeiro de 2025
O Chrome 133 se baseia nas consultas de contêiner, introduzindo consultas de contêiner de estado de rolagem. O estado gerenciado pelo navegador para posicionamento fixo, pontos de ajuste de rolagem e elementos roláveis agora pode ser consultado e adaptado pelo CSS.
Visão geral
Antes das consultas de estado de rolagem, você precisa usar JavaScript para entender se um elemento está travado, fixado ou rolável. Agora há um método mais eficiente na faixa de padrões para saber essas informações e se adaptar de acordo. Há também uma nova maneira de acionar animações, liberando a animação acionada por rolagem do CSS.
Confira uma visão geral das consultas de estado disponíveis no Chrome 133:
- Estado travado:
- Aciona mudanças de estilo quando um elemento está preso a uma borda.
- Estado fixado:
- Aciona mudanças de estilo quando um elemento é fixado em um eixo.
- Estado rolável:
- Aciona mudanças de estilo quando um elemento está transbordando.
A boa notícia é que tudo o que você aprendeu com as consultas de contêiner vai ajudar você a trabalhar com consultas de estado de rolagem.
Também há um território desconhecido entre as animações de rolagem e as consultas de contêiner de estado de rolagem. Precisamos experimentar o tempo e o contexto para descobrir se uma animação de rolagem ou uma animação de estado de rolagem acionada por rolagem será a melhor. O vídeo e a demonstração a seguir ilustram o problema: uma animação acionada por adesão em comparação com uma animação gerada por rolagem.
Primeira consulta de estado de rolagem
A primeira etapa é definir o contêiner usando um novo valor para a propriedade container-type
. Assim como em uma consulta de contêiner, o elemento que você quer consultar é aquele que recebe o container-type
e, opcionalmente, um container-name
. Com as consultas de estado de rolagem, você fornece o container-type: scroll-state
do elemento que está fixado, travado ou com transbordamento.
.stuck-top {
container-type: scroll-state;
position: sticky;
top: 0px;
}
A segunda etapa é selecionar o filho desse contêiner que vai responder ao estado. Com as consultas de contêiner, não é possível usar o mesmo elemento que tem o container-type
.
.stuck-top {
container-type: scroll-state;
position: sticky;
top: 0px;
> nav {
@container scroll-state(stuck: top) {
background: Highlight;
color: HighlightText;
}
}
}
A terceira etapa é testá-lo. O exemplo de CSS a seguir vai estilizar o plano de fundo em vermelho quando o elemento .stuck-top
ficar preso ao topo em 0
. Com algumas linhas extras no CSS que já escrevemos e um elemento extra que contém o estado do navegador, nossos componentes são muito mais inteligentes em relação ao que os cerca.
Aprimoramento progressivo
A regra @supports
e o aninhamento permitem adicionar o aprimoramento progressivo ou o uso condicional de recursos em apenas algumas linhas de código:
.stuck-top {
container-type: scroll-state;
position: sticky;
top: 0px;
@supports (container-type: scroll-state) {
> nav {
@container scroll-state(stuck: top) {
background: Highlight;
color: HighlightText;
}
}
}
}
Além disso, lembre-se de usar @media (prefers-reduced-motion: no-preference) {}
em torno do movimento, se você animar elementos na página com consultas de estado de rolagem.
Casos de uso
Preso(a)
Talvez essa seção devesse ser chamada de "situações fixas"? Esta é uma pequena coleção de casos de uso de estado fixo, além de uma seção bônus de ideias que precisam ser criadas.
@container scroll-state(stuck: top) {}
@container scroll-state(stuck: bottom) {}
Adicionar uma sombra quando estiver preso
Um dos casos de uso mais comuns para uma consulta bloqueada é para barras de navegação que querem adicionar box-shadow
quando estão bloqueadas, para que possam aparecer flutuando sobre o conteúdo que estão sobrepondo.
.stuck-top {
container-type: scroll-state;
position: sticky;
top: 0px;
> nav {
transition: box-shadow .3s ease;
@container scroll-state(stuck: top) {
box-shadow: var(--shadow-5);
}
}
}
Ativar o cabeçalho atual.
Outro cenário comum de feedback de interface fixa é destacar o elemento fixado. Em uma lista de bandas em ordem alfabética, isso pode ser muito útil e contribuir para a experiência.
.sticky-slide {
dt {
container-type: scroll-state;
position: sticky;
inset-block-start: 0;
inset-inline: 0;
> header {
transition:
background .3s ease,
box-shadow .5s ease;
@container scroll-state(stuck: top) {
background: hsl(265 100% 27%);
box-shadow: 0 5px 5px #0003;
}
}
}
}
Aqui está outra variante, em que os cabeçalhos estão ao lado dos itens da lista. Muitas possibilidades.
Estouro de ideias
Confira uma lista de demonstrações fixas que podem inspirar você a dar um toque especial ou remover o JavaScript delas com consultas de estado de rolagem. Sugiro que você tente criar um que você goste. Isso vai ajudar a sintaxe e as ideias a ficarem fixadas 😏.
- https://codepen.io/BlogFire/pen/PoGMjaX: variante de notas autoadesivas.
- https://codepen.io/mikegolus/pen/jOZzRzw: adiciona sombras a uma tabela quando elas ficam presas.
- https://codepen.io/MarcRay/pen/PomBeP: a barra de navegação do cabeçalho aparece no acionador
- https://codepen.io/kevinpowell/pen/OqKJjK - revelação de barra de navegação do rodapé
- https://codepen.io/abhisekz-the-decoder/pen/eKaLRd: cabeçalhos de cartão fixos
- https://codepen.io/tutsplus/pen/abojPjP - preço com sombra no cabeçalho do acionador
- https://codepen.io/kevinpowell/pen/KEjMEv: títulos da barra lateral da seção "sticky"
Capturado
Com as consultas de estado fixado, podemos remover parte da responsabilidade do JavaScript e dos eventos de ajuste e transferir o processamento para o CSS.
@container scroll-state(snapped: x) {}
@container scroll-state(snapped: y) {}
@container scroll-state(snapped: inline) {}
@container scroll-state(snapped: block) {}
Um pequeno lembrete, caso você tenha pulado a seção Primeira consulta de estado de rolagem, o contêiner de uma consulta de ajuste é o elemento com scroll-snap-align
, e o elemento que pode se adaptar precisa ser filho desse elemento. Isso significa que há três elementos necessários para configurar isso:
a scroll container with `scroll-snap-type`
⤷ a snap target with both `scroll-snap-align` and `container-type: scroll-state`
⤷ a child of the snap target that can query the container for snap state
Melhorar visualmente o item encaixado
É muito comum que um controle deslizante fixado no centro destaque ou mostre o item fixado no centro. Neste exemplo de depoimentos, a palavra-chave not
é usada para que todos os depoimentos desativados tenham baixa opacidade, enquanto os ativados permanecem no estado de apresentação natural.
.demo {
overflow: auto hidden;
scroll-snap-type: x mandatory;
> article {
container-type: scroll-state;
scroll-snap-align: center;
@supports (container-type: scroll-state) {
> * {
transition: opacity .5s ease;
@container not scroll-state(snapped: x) {
opacity: .25;
}
}
}
}
}
Mostrar a legenda do item fixado
Este é um bom exemplo de como as consultas de estado de rolagem ativam a animação acionada pelo rolagem. É também um bom exemplo de quando respeitar a redução de movimento é importante no CSS.
.demo {
overflow-x: auto;
scroll-behavior-x: contain;
scroll-snap-type: x mandatory;
> .card {
container-type: scroll-state;
scroll-snap-align: center;
@supports (container-type: scroll-state) {
@media (prefers-reduced-motion: no-preference) {
figcaption {
transform: translateY(100%);
@container scroll-state(snapped: x) {
transform: translateY(0);
}
}
}
}
}
}
Como animar elementos de slides
É muito comum animar elementos de uma apresentação de slides ou de uma palestra. Era muito chato escrever um observador de interseção para isso, que apenas definia uma classe no slide. Agora não precisamos de JavaScript.
html {
scroll-snap-type: y mandatory;
}
section {
container-type: scroll-state;
scroll-snap-align: start;
scroll-snap-stop: always;
@supports (container-type: scroll-state) {
@media (prefers-reduced-motion: no-preference) {
> h1 {
transition: opacity .5s ease, transform .5s var(--ease-spring-3);
transition-delay: .5s;
opacity: 0;
transform: scale(1.25);
@container scroll-state(snapped: block) {
opacity: 1;
transform: scale(1);
}
}
}
}
}
Todas as consultas de estado do CSS fixadas se comportam como scrollsnapchanging
, e não como scrollsnapchange
. Isso oferece o gancho mais rápido possível para fornecer feedback visual do elemento fixado. Se ele for muito rápido, considere o evento JavaScript.
Rolável
A consulta de estado rolável será muito útil para mostrar recursos visuais quando uma área de rolagem pode ser rolada. Até as consultas de estado de rolagem, essa era uma informação difícil de saber.
@container scroll-state(scrollable: top) {}
@container scroll-state(scrollable: right) {}
@container scroll-state(scrollable: bottom) {}
@container scroll-state(scrollable: left) {}
Indicar rolagem com sombras
Há um famoso truque de CSS de Lea Verou que usa background-attachment: local
para conseguir um efeito semelhante a esse, além de uma maneira de fazer isso com animação orientada por rolagem. Cada técnica tem suas vantagens e desvantagens. Cabe a nós descobrir quando e onde cada uma delas é mais adequada.
O exemplo a seguir usa um único elemento fixo que abrange o scrollport. Um gradiente na parte de cima e outro na parte de baixo têm a opacidade animada com @property
quando a consulta de estado de rolagem contextual é aplicada: @container scroll-state(scrollable: top)
.
Ele é o primeiro contêiner que é size
e scroll-state
.
.scroll-container {
container-type: scroll-state size;
overflow: auto;
&::after {
content: " ";
background: var(--_shadow-top), var(--_shadow-bottom);
transition:
--_scroll-shadow-color-1-opacity .5s ease,
--_scroll-shadow-color-2-opacity .5s ease;
@container scroll-state(scrollable: top) {
--_scroll-shadow-color-1-opacity: var(--_shadow-color-opacity, 25%);
}
@container scroll-state(scrollable: bottom) {
--_scroll-shadow-color-2-opacity: var(--_shadow-color-opacity, 25%);
}
}
}
Comando de seta
Às vezes, mostrar uma seta pode ajudar os usuários a descobrir que uma área pode ser rolada. Elas tendem a apontar para a direção em que a rolagem pode ocorrer e desaparecem quando não são mais necessárias. Você pode fazer isso com o seguinte código.
@container scroll-state((scrollable: top) or (not (scrollable: bottom))) {
translate: 0 calc(100% + 10px);
}
@container scroll-state((scrollable: top) and (not (scrollable: bottom))) {
translate: 0 calc(100% + 10px);
rotate: .5turn;
}
Voltar ao início
Outra interação de estado de rolagem comum é o botão de conveniência "rolar para cima". O código a seguir faz com que o botão de rolagem para cima desapareça quando não há mais nada para rolar para cima.
Essa solução é um pouco invertida, mas permite reduzir a quantidade de CSS. O botão fica visível quando está em repouso, então você precisa dizer a ele para se esconder quando não houver mais para onde rolar para cima.
@container not scroll-state(scrollable: top) {
translate: 0 calc(100% + 10px);
}
Continuação do estudo
Se você quiser mais informações, confira alguns recursos que vão desde detalhes de especificações até outros ótimos artigos sobre o assunto:
- O que mais podemos consultar no contêiner? https://github.com/w3c/csswg-drafts/issues/5989
- Explicação de scroll-state() - https://drafts.csswg.org/css-conditional-5/scroll_state_explainer.md
- Especificação CSS scroll-state() https://www.w3.org/TR/css-conditional-5/#scroll-state-container
- Captura de tela do layout no loop de eventos HTML
- Um episódio de podcast sobre consultas de estado: https://nerdy.dev/the-css-podcast-on-state-queries
- Mais artigos
- https://utilitybend.com/blog/is-the-sticky-thing-stuck-is-the-snappy-item-snapped-a-look-at-state-queries-in-css/
- https://ishadeed.com/article/css-state-queries/
- https://csscade.com/can-you-detect-overflow-with-css/
- https://css-tip.com/overflow-detection/: como detectar com animação de rolagem de uma forma que mais do que apenas crianças possam saber (com a troca de truques)