In modern web design, users expect smooth, immersive experiences. A common pattern is a full-screen slide interface—sections or images that occupy the entire viewport and snap into place as the user scrolls. While many developers reach for JavaScript-heavy slider libraries, CSS Scroll Snap offers a lightweight, native way to build this effect with minimal script.
In this guide, you’ll learn how to:
-
configure a snapping container
-
align slides to fill the viewport
-
add smooth transitions and navigation
-
address cross-browser quirks
-
optimize for performance and accessibility
What Is CSS Scroll Snap?
CSS Scroll Snap is a set of CSS properties that let you define “snap points” along a scrollable container’s scroll axis. When the user scrolls (via mouse, touch, trackpad, etc.), the viewport can automatically “snap” to predefined points (usually the start, center, or end of child elements).
Key properties include:
-
scroll-snap-type (on the container)
-
scroll-snap-align (on the children)
-
scroll-snap-stop, scroll-padding, scroll-margin for more control
By combining these, you can make the scroll settle exactly on each slide, avoiding partially cut-off content.
Step-by-Step: Building a Full-Screen Slideshow
1. HTML Structure
A straightforward HTML scaffold looks like this:
<body>
<div class="slideshow-container">
<section class="slide">Slide 1 content</section>
<section class="slide">Slide 2 content</section>
<section class="slide">Slide 3 content</section>
<!-- more slides as needed -->
</div>
</body>
Each .slide becomes a full-viewport panel. You can include images, text, overlays, etc., inside each.
2. Base CSS: Full Viewport + Scrolling Context
/* Ensure html/body fill the viewport, remove default margin */
html, body {
margin: 0;
height: 100%;
width: 100%;
overflow: hidden; /* Prevent body scroll if you want scroll only in container */
}
/* Slideshow container: scrollable and snapping */
.slideshow-container {
height: 100vh;
width: 100vw;
overflow-y: scroll; /* vertical scroll; change to overflow-x for horizontal mode */
scroll-snap-type: y mandatory;
/* optional: hide default scrollbar visuals */
scrollbar-width: none; /* Firefox */
}
.slideshow-container::-webkit-scrollbar {
display: none; /* Chrome, Safari, Edge */
}
/* Individual slides */
.slide {
scroll-snap-align: start;
height: 100vh;
width: 100vw;
/* For layout inside slides */
display: flex;
justify-content: center;
align-items: center;
}
In this setup:
-
The container is exactly the viewport (100vh/100vw).
-
Each slide also spans full viewport.
-
scroll-snap-type: y mandatorymakes vertical snapping required. -
The children snap at their start edges (
scroll-snap-align: start).
If you prefer horizontal sliding, replace overflow-y: scroll with overflow-x: scroll, and use scroll-snap-type: x mandatory. Also set .slide { flex: 0 0 100vw; } and make .slideshow-container a flex container (display: flex) so slides line up horizontally.
3. Smooth Scrolling
Adding smooth transitions makes the snapping visually nicer:
.slideshow-container {
scroll-behavior: smooth;
}
This way, when a user scrolls (or you programmatically scroll), the movement is animated rather than instant.
4. Optional Navigation Buttons / Indicators
To allow users to jump to a slide:
<div class="nav">
<button data-slide="0">1</button>
<button data-slide="1">2</button>
<button data-slide="2">3</button>
</div>
const container = document.querySelector('.slideshow-container');
const slides = container.querySelectorAll('.slide');
const navButtons = document.querySelectorAll('.nav button');
navButtons.forEach(btn => {
btn.addEventListener('click', () => {
const idx = parseInt(btn.getAttribute('data-slide'), 10);
slides[idx].scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
This JavaScript simply scrolls the container so the selected slide comes into view. Because scroll-behavior: smooth is set, the motion is graceful.
5. Handling Browser & Spec Nuances
-
Use
@supports (scroll-snap-align: start)to detect modern scroll snap support and provide fallbacks. -
On iOS, ensure
-webkit-overflow-scrolling: touch;is set on the container to support smooth native scrolling. -
Use
scroll-snap-stop: alwayson slides you don’t want skipped (i.e. force a stop there). -
If using a fixed header or footer, use
scroll-padding-topon the container to offset snap positions so content isn’t hidden beneath UI.
Variations & Advanced Techniques
Horizontal + Vertical (2D) Slideshows
You can create grid-like slides where each slide is navigable in both directions:
.slideshow-container {
scroll-snap-type: both mandatory;
overflow: scroll;
}
.slide {
scroll-snap-align: center;
width: 100vw;
height: 100vh;
}
In this mode, sliding left/right or up/down can land you on new slides. Use with care, as user expectations of scroll direction can conflict.
Nested Snap Zones
You might have a vertical page, where one section has a horizontal scroll snap gallery. For example:
<section class="section">
<!-- normal page content -->
</section>
<section class="gallery-section">
<div class="gallery">
<div class="gallery-item">…</div>
<div class="gallery-item">…</div>
</div>
</section>
Here, .gallery has scroll-snap-type: x mandatory and overflow-x: auto, while the outer page scroll uses its own snapping. In practice, event propagation and scroll boundaries must be managed carefully to avoid conflicts.
Dynamic Content & Responsive Slides
If slide content changes size or you dynamically inject slides, you may need to recalculate scroll positions or sometimes reset scroll offset. A simple approach is to:
window.addEventListener('resize', () => {
// optional: snap to nearest slide or reset scroll
});
Or use IntersectionObserver to detect when a slide enters view, then adjust classes or layout accordingly.
Performance & Accessibility Considerations
-
Lazy-load images/videos: Use
loading="lazy"or intersection-based loading so slides off-screen don’t lag the scroll. -
Minimize heavy animation: Don’t animate large CSS properties during scroll; let the browser optimize.
-
Provide alternate navigation: Don’t rely entirely on scroll; include keyboard navigation, skip links, or a table-of-contents.
-
Ensure focus management: Slides with interactive content (buttons, forms) should be focusable and visible once snapped.
-
Test on touch & trackpad: Scroll snap can feel different on different input devices; iterate for natural feeling.
Example: Vertical Full-Screen Slideshow (Complete Code)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Fullscreen Scroll Snap Slideshow</title>
<style>
html, body {
margin: 0;
height: 100%;
width: 100%;
overflow: hidden;
}
.slideshow-container {
height: 100vh;
width: 100vw;
overflow-y: scroll;
scroll-snap-type: y mandatory;
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
}
.slideshow-container::-webkit-scrollbar {
display: none;
}
.slide {
scroll-snap-align: start;
height: 100vh;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
font-size: 3rem;
}
.slide:nth-child(odd) {
background-color: #333;
color: #fff;
}
.slide:nth-child(even) {
background-color: #f4f4f4;
color: #222;
}
.nav {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
z-index: 100;
}
.nav button {
margin: 0 5px;
padding: 8px 12px;
font-size: 1rem;
}
</style>
</head>
<body>
<div class="slideshow-container">
<section class="slide">Slide One</section>
<section class="slide">Slide Two</section>
<section class="slide">Slide Three</section>
<section class="slide">Slide Four</section>
</div>
<div class="nav">
<button data-slide="0">1</button>
<button data-slide="1">2</button>
<button data-slide="2">3</button>
<button data-slide="3">4</button>
</div>
<script>
const container = document.querySelector('.slideshow-container');
const slides = container.querySelectorAll('.slide');
const navButtons = document.querySelectorAll('.nav button');
navButtons.forEach(btn => {
btn.addEventListener('click', () => {
const idx = parseInt(btn.getAttribute('data-slide'), 10);
slides[idx].scrollIntoView({ behavior: 'smooth', block: 'start' });
});
});
</script>
</body>
</html>
This code gives you a vertical, full-screen slideshow with clickable navigation dots.
Conclusion
CSS Scroll Snap makes it surprisingly straightforward to build a smooth, full-screen slideshow with minimal JavaScript. The approach is lightweight, responsive, and taps into browser-native scroll behavior.
When implementing, keep the following in mind:
-
Choose an axis (vertical or horizontal) and ensure your container and slides fill the viewport.
-
Use
scroll-snap-typeandscroll-snap-alignto define snapping behavior. -
Layer in
scroll-behavior: smoothfor graceful transitions. -
Add navigation and accessibility considerations for real-world usage.
-
Test across devices (touch, mouse, keyboard) and browsers.
Once you’ve got this pattern working, it's easy to extend: add pagination indicators, autoplay via script, nested slides, or custom easing. Use it in portfolios, storytelling sites, image galleries, or anywhere you want to guide the user’s scroll in a controlled, immersive way.