Karussell-Snap-Highlights

Mit Containerabfragen für den Scrollstatus können Sie Elemente basierend auf ihrem aktuellen Scrollstatus formatieren, z. B. ob ein Element „fixiert“ (über die fixierte Positionierung) oder „eingerastet“ (über Scroll Snap) ist. So lassen sich Karussell- oder Galerie-Ansichten erstellen, bei denen das aktive Element visuell hervorgehoben werden kann, ohne dass JavaScript-Intersection-Observer oder Scroll-Event-Listener erforderlich sind.

Grundlegende Implementierung

Wenn Sie eingerastete Elemente hervorheben möchten, müssen Sie einen Scroll-Snap-Container einrichten, die Einrastziele als Scroll-Status-Container definieren und dann diesen Status abfragen, um untergeordnete Elemente zu gestalten.

1. Scroll-Snap-Container einrichten

Für den übergeordneten Container muss scroll-snap-type aktiviert sein.

<div class="carousel">
  <div class="carousel-item">
    <div class="card">Product 1 content</div>
  </div>
  <div class="carousel-item">
    <div class="card">Product 2 content</div>
  </div>
</div>
.carousel {
  display: flex;
  overflow-x: auto;
  /* MANDATORY: Enable scroll snapping on the container */
  scroll-snap-type: x mandatory;
}

2. Andockziele als Scrollstatus-Container definieren

Jedes Element im Karussell, das für das Einrasten erfasst werden soll, muss als scroll-state-Container deklariert werden.

.carousel-item {
  /* Define where the item snaps within the container */
  scroll-snap-align: center;
  
  /* MANDATORY: Establish this element as a scroll-state query container */
  container-type: scroll-state;
}

3. snapped-Status abfragen

Da Containerabfragen Nachfolger formatieren, müssen Sie die Hervorhebungsstile auf ein Element innerhalb des Snap-Ziels anwenden. Da der Scroll-Container so eingestellt ist, dass er auf der x-Achse überläuft, verwenden Sie die Abfrage scroll-state(snapped: x).

VERPFLICHTEND: Schließen Sie die Stile in @media (prefers-reduced-motion: no-preference) ein, damit der Effekt nur Nutzern angezeigt wird, die keine reduzierte Bewegung angefordert haben. Je nach Anwendungsfall können Sie Teile des Effekts beibehalten. In diesem Fall blinken die Karten jedoch von weiß zu blau, was für einige Nutzer Probleme verursachen kann. Daher deaktivieren wir den Effekt vollständig.

/* Specify transition outside of queries so that it is applied regardless of state.  */
.card {
  transition:
    scale 0.4s cubic-bezier(0.25, 0.8, 0.25, 1),
    background-color 0.4s,
    color 0.4s,
    box-shadow 0.4s;
}
/* 
Only show the effect for users not requesting reduced motion. Disable completely, including the color change, as it causes a flash that may be problematic.
*/
@media (prefers-reduced-motion: no-preference) {
  /* Style the content when its parent .carousel-item is snapped on the x axis */
  @container scroll-state(snapped: x) {
    .card {
      background: #007bff;
      color: white;
      scale: 1.15;
      box-shadow: 0 10px 25px rgba(0, 123, 255, 0.3);
    }
  }
}

/* MANDATORY Copy-Paste Safety: Disable highlight scaling/flashing for motion sensitive users */
@media (prefers-reduced-motion: reduce) {
  .card {
    transition: none !important;
    scale: 1 !important;
  }
}

Mit dem Deskriptor snapped können bestimmte Achsen abgefragt werden: x, y, inline, block oder both.

Bedienungshilfen

AVOID: scroll-state mit interaktiven Elementen verwenden.

Visuelle Hervorhebungen für angedockte Elemente können die UX verbessern, aber das angedockte Element wird nicht im Accessibility Tree angezeigt. Das visuelle Design, das auf ein angedocktes Element angewendet wird, sollte nicht den Eindruck erwecken, dass das Element aktiv oder im Fokus ist. Ein Tastaturfokusring sollte gut sichtbar und von der snapped-Hervorhebung unterscheidbar sein. Wenn das angedockte Element interaktiv ist, müssen Sie andere Standardverfahren für Bedienungshilfen verwenden, um es barrierefrei zu machen.

Das Einrasten erfolgt durch Scrollen, wodurch der Tastaturfokus nicht verschoben wird. Der Tastaturfokus kann jedoch dazu führen, dass sich der Scroll-Container bewegt, was zu einer Änderung des angedockten Elements führt, das das fokussierte Element sein kann oder auch nicht. Dies führt wahrscheinlich zu Verwirrung bei den Nutzern und wird nicht empfohlen.

Fallback-Strategien

Für Browser, die keine Scrollstatusabfragen unterstützen, sollten Sie eine funktionale Basisversion bereitstellen, in der alle Elemente auch ohne die Hervorhebung „aktiv“ lesbar sind.

Feature-Erkennung

Mit @supports können Sie nur unterstützten Browsern Verbesserungen bereitstellen:

@supports (container-type: scroll-state) {
  /* Enhancement styles here */
}

JavaScript-Fallback

Wenn die Hervorhebung für die Nutzererfahrung entscheidend ist, verwenden Sie IntersectionObserver, um das eingerastete Element zu bestimmen. Passen Sie den beobachteten Bereich auf einen dünnen Ausschnitt in der Mitte des Karussells an, indem Sie einen rootMargin mit einem negativen Inline-Wert angeben. Wenn Sie beispielsweise ein Element als überschneidend betrachten möchten, wenn es sich in den mittleren 2 % des Karussells befindet, setzen Sie rootMargin auf "0px -49%".

// Optional: detect support and apply a JS-based fallback
if (!CSS.supports('container-type', 'scroll-state')) {
  const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
      // Toggle a class based on intersection
      entry.target.classList.toggle('is-snapped', entry.isIntersecting);
    });
  }, {
    root: document.querySelector(".carousel"),
    // Carousel item intersects if any part of the carousel item is in the middle 2% of the carousel.
    rootMargin: "0px -49%"
  });

  document.querySelectorAll('.carousel-item').forEach(item => {
    observer.observe(item);
  });
}