StealThis .dev

CSS Scroll-Snap Carousel

A dependency-free horizontal carousel powered entirely by CSS scroll-snap with mandatory snapping, snap-stop to prevent skipping, and native ::scroll-marker navigation dots that generate themselves from the slides. A progressive-enhancement @supports fallback swaps in anchor-link dots for browsers without scroll-marker-group, and a small JS layer only adds a live progress readout and previous/next controls.

Open in Lab
css
Targets: JS HTML

Code

A horizontal carousel whose core behavior needs zero JavaScript. The track is a scroll-snap-type: x mandatory scroll container and each slide declares scroll-snap-align: center plus scroll-snap-stop: always, so a single flick always lands on exactly one slide instead of flying past several. The navigation dots are generated by the browser itself using the CSS Carousel primitives ::scroll-marker and scroll-marker-group — clicking a marker smooth-scrolls to its slide and the active marker is styled via :target-current, all without a line of script.

Because ::scroll-marker is still rolling out, an @supports not (…) block feature-detects it and swaps in a row of plain anchor-link dots (<a href="#slide-3">) that reach the same slides through fragment navigation and scroll-behavior: smooth. Either way the carousel is keyboard operable, focus-visible, and reduced-motion aware.

The optional script.js layer is pure enhancement: an IntersectionObserver tracks which slide is centered to drive a live “3 / 6” counter and a progress bar, and previous/next buttons call scrollIntoView. Remove the script entirely and the carousel — snapping, dots, and all — keeps working. It demonstrates the modern CSS Scroll Snap and CSS Carousel (scroll markers) web platform APIs.