Conseils sur l'affordance de défilement

Présentation

Les indices visuels, comme les ombres ou les dégradés, aident les utilisateurs à comprendre qu'ils peuvent faire défiler l'écran pour voir plus de contenu. Ce guide explique comment créer ces indices à l'aide de la propriété CSS container-scroll-state-queries, qui permet de styliser les éléments en fonction de l'état de défilement de leur conteneur sans avoir recours à des écouteurs ou des observateurs de défilement JavaScript.

Implémentation

1. Établir le conteneur de défilement

Le conteneur de défilement doit être déclaré en tant que conteneur de requête d'état de défilement.

.scroller {
  overflow-y: auto;
  /* Establish this element as a scroll-state query container */
  container-type: scroll-state;
  position: relative;
}

2. Styliser les indicateurs

Placez les éléments d'indication (comme les ombres, les dégradés ou les flèches) dans le conteneur et appliquez-leur un style. Par défaut, ils ne doivent pas être visibles. Lorsqu'ils sont affichés, ils ne doivent pas être interactifs, en définissant pointer-events: none.

.indicator-top, .indicator-bottom {
  position: sticky;
  left: 0;
  right: 0;
  height: 20px;
  opacity: 0;
  transition: opacity 0.2s;
  pointer-events: none; /* Let clicks pass through */
}

.indicator-top {
  top: 0;
  background: linear-gradient(to bottom, rgba(0,0,0,0.2), transparent); /* Example: Shadow */
}

.indicator-bottom {
  bottom: 0;
  background: linear-gradient(to top, rgba(0,0,0,0.2), transparent); /* Example: Shadow */
}

3. Interroger l'état de défilement

Utilisez la règle @container avec la fonction scroll-state. Vérifiez si le conteneur peut défiler vers le haut ou vers le bas pour afficher l'indicateur correspondant.

/* Show top indicator when the user can scroll up */
@container scroll-state(scrollable: top) {
  .indicator-top {
    opacity: 1;
  }
}

/* Show bottom indicator when the user can scroll down */
@container scroll-state(scrollable: bottom) {
  .indicator-bottom {
    opacity: 1;
  }
}

Stratégies de remplacement

Remplacement de base

Si la fonctionnalité n'est pas prise en charge, les indicateurs resteront invisibles. Comme il s'agit d'indications et qu'elles ne sont pas essentielles au fonctionnement, vous pouvez les omettre dans les navigateurs non compatibles.

Solution de remplacement avancée (Intersection Observer)

Si les indices sont requis, utilisez un IntersectionObserver pour activer/désactiver les classes lorsque les éléments sentinelles en haut et en bas du défilement entrent et sortent du port de défilement.

<!-- Sentinel elements placed at the ends of the scroller -->
<div class="sentinel-top"></div>
<!-- Content goes here -->
<div class="sentinel-bottom"></div>
/* Marker styling to ensure it does not affect layout */
.sentinel-top, .sentinel-bottom {
  height: 0;
  width: 0;
  visibility: hidden;
}

.scroller.scrolled-down .indicator-top {
  opacity: 1;
}

.scroller.can-scroll-down .indicator-bottom {
  opacity: 1;
}
if (!CSS.supports('container-type', 'scroll-state')) {
  const topSentinel = document.querySelector('.sentinel-top');
  const bottomSentinel = document.querySelector('.sentinel-bottom');
  const scroller = document.querySelector('.scroller');

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      if (entry.target === topSentinel) {
        // If top sentinel is not intersecting, we have scrolled down
        scroller.classList.toggle('scrolled-down', !entry.isIntersecting);
      }
      if (entry.target === bottomSentinel) {
        // If bottom sentinel is intersecting, we reached the bottom
        scroller.classList.toggle('can-scroll-down', !entry.isIntersecting);
      }
    });
  }, { root: scroller });

  observer.observe(topSentinel);
  observer.observe(bottomSentinel);
}