Scroll Snap
CSS Scroll Snap provides native scroll snapping behavior, eliminating the need for heavy JavaScript carousel libraries.
The Old Way
Section titled “The Old Way”<div class="carousel" id="carousel"> <div class="carousel-track"> <div class="slide">Slide 1</div> <div class="slide">Slide 2</div> <div class="slide">Slide 3</div> </div> <button class="prev">←</button> <button class="next">→</button></div>// JavaScript carousel implementationclass Carousel { constructor(element) { this.carousel = element; this.track = element.querySelector('.carousel-track'); this.slides = element.querySelectorAll('.slide'); this.currentIndex = 0;
element.querySelector('.prev').addEventListener('click', () => this.prev()); element.querySelector('.next').addEventListener('click', () => this.next());
// Touch handling let startX; this.track.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; }); this.track.addEventListener('touchend', (e) => { const diff = startX - e.changedTouches[0].clientX; if (Math.abs(diff) > 50) { diff > 0 ? this.next() : this.prev(); } }); }
goTo(index) { this.currentIndex = Math.max(0, Math.min(index, this.slides.length - 1)); this.track.style.transform = `translateX(-${this.currentIndex * 100}%)`; }
prev() { this.goTo(this.currentIndex - 1); } next() { this.goTo(this.currentIndex + 1); }}
new Carousel(document.getElementById('carousel'));Problems:
- Large JavaScript bundle for carousel library
- Complex touch event handling
- Manual scroll position calculations
- Accessibility often overlooked
- Performance issues with many slides
- Different libraries, different APIs
The Modern Way
Section titled “The Modern Way”.carousel { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth; -webkit-overflow-scrolling: touch;}
.slide { flex: 0 0 100%; scroll-snap-align: start;}
/* Hide scrollbar but keep functionality */.carousel { scrollbar-width: none;}.carousel::-webkit-scrollbar { display: none;}<!-- Simple HTML - no JavaScript needed for basic functionality --><div class="carousel"> <div class="slide">Slide 1</div> <div class="slide">Slide 2</div> <div class="slide">Slide 3</div></div>Benefits:
- Zero JavaScript for basic carousel
- Native touch and mouse scrolling
- Built-in momentum scrolling
- Keyboard accessible (arrow keys)
- Better performance
- Smaller bundle size
Scroll Snap Properties
Section titled “Scroll Snap Properties”Container Properties
Section titled “Container Properties”.container { /* Required: Enable scroll snapping */ scroll-snap-type: x mandatory; /* or */ scroll-snap-type: y proximity; /* or */ scroll-snap-type: both mandatory;
/* Scroll padding - offset snap positions */ scroll-padding: 20px; scroll-padding-inline: 50px;}| Value | Behavior |
|---|---|
mandatory | Always snaps to nearest snap point |
proximity | Snaps when close to a snap point |
x | Horizontal scrolling |
y | Vertical scrolling |
both | Both directions |
Child Properties
Section titled “Child Properties”.item { /* Where this element snaps */ scroll-snap-align: start; /* or */ scroll-snap-align: center; /* or */ scroll-snap-align: end;
/* Prevent snapping past this element */ scroll-snap-stop: always;}Practical Examples
Section titled “Practical Examples”Horizontal Carousel
Section titled “Horizontal Carousel”.carousel { display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: 24px; padding: 24px;}
.carousel-item { flex: 0 0 300px; scroll-snap-align: start;}
/* Show multiple items */.carousel-item { flex: 0 0 calc(33.333% - 12px);}Full-Page Sections
Section titled “Full-Page Sections”html { scroll-snap-type: y mandatory;}
section { height: 100vh; scroll-snap-align: start; scroll-snap-stop: always;}Image Gallery
Section titled “Image Gallery”.gallery { display: flex; overflow-x: auto; scroll-snap-type: x mandatory;}
.gallery img { flex: 0 0 100%; height: 60vh; object-fit: contain; scroll-snap-align: center;}Card Carousel with Peek
Section titled “Card Carousel with Peek”.card-carousel { display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; padding: 20px; /* Show next card peeking */ scroll-padding-inline-start: 20px;}
.card { flex: 0 0 calc(100% - 60px); scroll-snap-align: start;}
@media (min-width: 768px) { .card { flex: 0 0 calc(50% - 28px); }}Vertical Story Viewer
Section titled “Vertical Story Viewer”.story-container { height: 100vh; overflow-y: auto; scroll-snap-type: y mandatory;}
.story { height: 100vh; scroll-snap-align: start; scroll-snap-stop: always;}Tabs with Scroll Snap
Section titled “Tabs with Scroll Snap”.tabs-content { display: flex; overflow-x: hidden; scroll-snap-type: x mandatory; scroll-behavior: smooth;}
.tab-panel { flex: 0 0 100%; scroll-snap-align: start;}
/* Navigate via anchor links *//* <a href="#panel-2">Tab 2</a> */Horizontal Scrolling Timeline
Section titled “Horizontal Scrolling Timeline”.timeline { display: flex; gap: 32px; overflow-x: auto; scroll-snap-type: x proximity; padding: 40px 20px;}
.timeline-event { flex: 0 0 250px; scroll-snap-align: center;}Adding Minimal JavaScript
Section titled “Adding Minimal JavaScript”// Optional: Dot indicators and prev/next buttonsconst carousel = document.querySelector('.carousel');const items = carousel.querySelectorAll('.carousel-item');
// Scroll to specific itemfunction goToSlide(index) { items[index].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest', });}
// Detect current slidecarousel.addEventListener('scrollend', () => { const scrollLeft = carousel.scrollLeft; const itemWidth = items[0].offsetWidth; const currentIndex = Math.round(scrollLeft / itemWidth); // Update indicators...});Browser Support
Section titled “Browser Support”Supported in all modern browsers. See caniuse.com/css-snappoints.
scroll-snap-stop has good support. See caniuse.com/mdn-css_properties_scroll-snap-stop.
Performance Considerations
Section titled “Performance Considerations”- Native implementation is highly optimized
- Uses GPU acceleration for smooth scrolling
- No JavaScript execution during scroll
- Better battery life on mobile devices
- Works with virtualized lists
When to Use
Section titled “When to Use”Use Scroll Snap when:
- Building image carousels or galleries
- Creating horizontal scrolling sections
- Implementing full-page scroll experiences
- Building mobile-friendly card lists
- Creating tabbed content with swipe navigation
Add JavaScript when:
- You need dot indicators or navigation buttons
- Tracking current slide index
- Auto-play functionality
- Complex navigation requirements