Comme les requêtes de conteneur, mais pour les requêtes bloquées, figées et débordantes.
Publié le 15 janvier 2025
Chrome 133 s'appuie sur les requêtes de conteneur en introduisant des requêtes de conteneur d'état de défilement. L'état géré par le navigateur pour le positionnement persistant, les points d'ancrage de défilement et les éléments à faire défiler peut désormais être interrogé et adapté à partir de CSS.
Présentation
Avant les requêtes d'état de défilement, vous deviez utiliser JavaScript pour déterminer si un élément était bloqué, aligné ou à faire défiler. Il existe désormais une méthode plus performante sur le canal de normes pour connaître ces informations et s'adapter en conséquence. Une nouvelle méthode de déclenchement d'animations est également disponible, qui permet de déclencher des animations à partir du CSS.
Voici un aperçu des requêtes d'état disponibles à partir de Chrome 133:
- État bloqué:
- Le style de déclenchement change lorsqu'un élément est collé à un bord.
- État verrouillé:
- Déclenche des modifications de style lorsqu'un élément est aligné sur une axe.
- État de défilement:
- Le style du déclencheur change lorsqu'un élément déborde.
La bonne nouvelle est que tout ce que vous avez appris sur les requêtes de conteneur vous aidera à travailler avec les requêtes d'état de défilement.
Le territoire entre les animations déclenchées par le défilement et les requêtes de conteneur d'état de défilement est également inexploré. Nous devons tester le timing et le contexte pour déterminer si une animation déclenchée par le défilement ou une animation d'état de défilement déclenchée par le défilement est la meilleure. La vidéo et la démonstration suivantes illustrent la situation : une animation déclenchée par un élément persistant par rapport à une animation déclenchée par le défilement.
Première requête d'état de défilement
La première étape consiste à définir le conteneur, en utilisant une nouvelle valeur pour la propriété container-type
. Comme pour une requête de conteneur, l'élément que vous souhaitez interroger est celui auquel vous attribuez container-type
et éventuellement container-name
. Avec les requêtes d'état de défilement, vous indiquez l'élément qui s'ancre, est bloqué ou présente un débordement container-type: scroll-state
.
.stuck-top {
container-type: scroll-state;
position: sticky;
top: 0px;
}
La deuxième étape consiste à sélectionner l'enfant de ce conteneur qui répondra à l'état. Comme pour les requêtes de conteneur, il ne peut pas s'agir du même élément qui comporte le container-type
.
.stuck-top {
container-type: scroll-state;
position: sticky;
top: 0px;
> nav {
@container scroll-state(stuck: top) {
background: Highlight;
color: HighlightText;
}
}
}
La troisième étape consiste à l'essayer. L'exemple CSS suivant applique un style rouge à l'arrière-plan lorsque l'élément .stuck-top
se fixe en haut à 0
. Avec quelques lignes supplémentaires dans le CSS que nous aurions déjà écrit et un élément contenant supplémentaire qui met en proxy l'état du navigateur, nos composants sont beaucoup plus intelligents concernant leur environnement.
Amélioration progressive
La règle d'instruction et l'imbrication @supports
vous permettent d'ajouter une amélioration progressive ou une utilisation conditionnelle des fonctionnalités en seulement quelques lignes de code supplémentaires:
.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;
}
}
}
}
N'oubliez pas non plus d'utiliser @media (prefers-reduced-motion: no-preference) {}
autour de votre mouvement si vous animez des éléments autour de la page à l'aide de requêtes d'état de défilement.
Cas d'utilisation
Coincé
Peut-être que cette section devrait s'intituler "Situations délicates" ? Voici une petite collection de cas d'utilisation de l'état persistant, ainsi qu'une section bonus d'idées à développer.
@container scroll-state(stuck: top) {}
@container scroll-state(stuck: bottom) {}
Ajouter une ombre en cas de blocage
L'un des cas d'utilisation les plus courants d'une requête bloquée concerne les barres de navigation qui souhaitent ajouter box-shadow
lorsqu'elles sont bloquées, afin qu'elles puissent sembler flotter au-dessus du contenu qu'elles superposent.
.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);
}
}
}
Activer l'en-tête bloqué actuel
Un autre scénario courant de retour d'interface utilisateur persistant consiste à mettre en surbrillance l'élément actuellement bloqué. Dans une liste de groupes alphabétiques, cela peut être très utile et améliorer l'expérience.
.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;
}
}
}
}
Voici une autre variante, dans laquelle les en-têtes se trouvent à côté des éléments de liste. Les possibilités sont nombreuses.
Dépassement d'idées
Voici une liste de démonstrations persistantes qui pourraient vous inspirer pour ajouter un peu de piquant à la démonstration ou supprimer leur code JavaScript, avec des requêtes d'état de défilement. Je vous suggère d'essayer d'en créer un qui vous plaît. Cela vous aidera à retenir la syntaxe et les idées 😏.
- https://codepen.io/BlogFire/pen/PoGMjaX : variante des notes adhésives
- https://codepen.io/mikegolus/pen/jOZzRzw : ajouter des ombres à un tableau lorsqu'il est collé
- https://codepen.io/MarcRay/pen/PomBeP : barre de navigation sous l'en-tête qui s'affiche au déclencheur
- https://codepen.io/kevinpowell/pen/OqKJjK : barre de navigation du pied de page qui s'affiche
- https://codepen.io/abhisekz-the-decoder/pen/eKaLRd : en-têtes de carte persistants
- https://codepen.io/tutsplus/pen/abojPjP : ombre de l'en-tête de prix sur le déclencheur
- https://codepen.io/kevinpowell/pen/KEjMEv : titres de la barre latérale de section persistants
Intégré
Avec les requêtes d'état ancré, nous pouvons décharger une partie de la responsabilité de JavaScript et des événements d'ancrage, et transférer la gestion vers le CSS.
@container scroll-state(snapped: x) {}
@container scroll-state(snapped: y) {}
@container scroll-state(snapped: inline) {}
@container scroll-state(snapped: block) {}
Petit rappel, au cas où vous auriez ignoré la section Première requête d'ancrage, le conteneur d'une requête d'ancrage est l'élément contenant scroll-snap-align
, et l'élément pouvant s'adapter doit être un enfant de cet élément. Pour ce faire, vous avez besoin de trois éléments:
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
Mettre en valeur l'élément épinglé visuellement
Il est très courant, avec un défilement centré, de mettre en avant l'élément centré. Dans cet exemple de témoignages, le mot clé not
est utilisé pour que tous les témoignages non épinglés aient une faible opacité, tandis que l'épinglé reste dans son état de présentation naturel.
.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;
}
}
}
}
}
Afficher la légende de l'élément épinglé
Il s'agit d'un bon exemple de la façon dont les requêtes d'état de défilement permettent d'activer l'animation déclenchée par le défilement. Il s'agit également d'un bon exemple de cas où le respect de la réduction du mouvement est utile dans le 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);
}
}
}
}
}
}
Animer des éléments de diapositive
Il est très courant d'animer des éléments d'un diaporama ou d'une présentation lors d'une conférence. Il était auparavant assez pénible d'écrire un observateur d'intersection pour cela, qui ne faisait que définir une classe sur la diapositive. Nous n'avons plus besoin 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);
}
}
}
}
}
Vous remarquerez peut-être que toutes les requêtes d'état CSS figées se comportent comme scrollsnapchanging
, et non comme scrollsnapchange
. Vous disposez ainsi du hook le plus précoce possible pour fournir un retour visuel sur l'élément épinglé. Si elle est trop impatiente, envisagez l'événement JavaScript.
Défilante
La requête d'état de défilement est très utile pour afficher les affordances visuelles lorsque la zone de défilement peut effectivement être défilée. Avant les requêtes d'état de défilement, il était difficile de connaître ces informations.
@container scroll-state(scrollable: top) {}
@container scroll-state(scrollable: right) {}
@container scroll-state(scrollable: bottom) {}
@container scroll-state(scrollable: left) {}
Indique le défilement à l'aide d'ombres
Lea Verou a créé un célèbre astuce CSS qui utilise background-attachment: local
pour obtenir un effet semblable, ainsi qu'une autre méthode avec une animation basée sur le défilement. Chaque technique présente des compromis. C'est à nous de déterminer quand et où chacune de ces techniques est la plus adaptée.
L'exemple suivant utilise un seul élément persistant qui s'étend sur le scrollport. L'opacité d'un dégradé en haut et d'un dégradé en bas est animée avec @property
lorsque leur requête d'état de défilement contextuel s'applique: @container scroll-state(scrollable: top)
.
Notez également qu'il s'agit du premier conteneur qui est à la fois un conteneur size
et 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%);
}
}
}
Requête de flèche
Parfois, afficher une flèche peut aider les utilisateurs à découvrir qu'une zone est déroulante. Ils ont tendance à indiquer la direction dans laquelle le défilement peut se produire et disparaissent une fois qu'ils ne sont plus nécessaires. Pour ce faire, utilisez le code suivant.
@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;
}
Haut de page
Une autre interaction populaire avec l'état de défilement est le bouton pratique "Défiler vers le haut". Le code suivant fait disparaître le bouton de défilement vers le haut lorsqu'il n'y a pas de défilement vers le haut.
Cette solution est un peu inversée, mais elle vous permet de réduire la quantité de CSS. Le bouton est visible par défaut. Vous devez donc lui indiquer de se masquer lorsqu'il n'est plus possible de faire défiler l'écran vers le haut.
@container not scroll-state(scrollable: top) {
translate: 0 calc(100% + 10px);
}
Étude continue
Pour en savoir plus, consultez les ressources suivantes, qui vont des détails des spécifications à d'autres articles intéressants sur ce sujet:
- Que pouvons-nous encore interroger dans les conteneurs ? https://github.com/w3c/csswg-drafts/issues/5989
- Explication de scroll-state() : https://drafts.csswg.org/css-conditional-5/scroll_state_explainer.md
- Spécification CSS scroll-state() : https://www.w3.org/TR/css-conditional-5/#scroll-state-container
- Capture d'instantanés de mise en page dans la boucle d'événements HTML
- Épisode de podcast sur les requêtes d'état : https://nerdy.dev/the-css-podcast-on-state-queries
- Autres articles
- 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/ : détection à l'aide d'une animation basée sur le défilement, de manière à ce que les enfants ne soient pas les seuls à pouvoir la voir (avec le compromis de la tromperie)