
Documentation
Webflow
Code
Setup: External Scripts
External Scripts in Webflow
Make sure to always put the External Scripts before the Javascript step of the resource.
In this video you learn where to put these in your Webflow project? Or how to include a paid GSAP Club plugin in your project?
HTML
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/Observer.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/CustomEase.min.js"></script>
Step 1: Copy structure to Webflow
Copy structure to Webflow
In the video below we described how you can copy + paste the structure of this resource to your Webflow project.
Copy to Webflow
Webflow structure is not required for this resource.
Step 1: Add HTML
HTML
<div data-slideshow="wrap" class="img-slider">
<div class="img-slider__list">
<div data-slideshow="slide" class="img-slide is--current"><img data-slideshow="parallax" alt="" src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf80921b87fe739e3cbd2_osmo-slideshow-img-3.avif" draggable="false" class="img-slide__inner"></div>
<div data-slideshow="slide" class="img-slide"><img data-slideshow="parallax" alt="" src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf809f124211fa71791fa_osmo-slideshow-img-1.avif" draggable="false" class="img-slide__inner"></div>
<div data-slideshow="slide" class="img-slide"><img data-slideshow="parallax" alt="" src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf809e5434a14205e65b2_osmo-slideshow-img-2.avif" draggable="false" class="img-slide__inner"></div>
<div data-slideshow="slide" class="img-slide"><img data-slideshow="parallax" alt="" src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf809fb316a147c9d1dda_osmo-slideshow-img-4.avif" draggable="false" class="img-slide__inner"></div>
</div>
<div class="img-slider__nav">
<div data-slideshow="thumb" class="img-slider__thumb is--current"><img src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf80921b87fe739e3cbd2_osmo-slideshow-img-3.avif" class="slider-thumb__img"></div>
<div data-slideshow="thumb" class="img-slider__thumb"><img src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf809f124211fa71791fa_osmo-slideshow-img-1.avif" class="slider-thumb__img"></div>
<div data-slideshow="thumb" class="img-slider__thumb"><img src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf809e5434a14205e65b2_osmo-slideshow-img-2.avif" class="slider-thumb__img"></div>
<div data-slideshow="thumb" class="img-slider__thumb"><img src="https://cdn.prod.website-files.com/67db521fe639fd09f860b106/67dbf809fb316a147c9d1dda_osmo-slideshow-img-4.avif" class="slider-thumb__img"></div>
</div>
</div>
HTML structure is not required for this resource.
Step 2: Add CSS
CSS
.img-slider {
grid-column-gap: 1rem;
grid-row-gap: 1rem;
border-radius: .5em;
justify-content: center;
align-items: flex-end;
width: 100%;
height: 100vh;
display: flex;
position: relative;
}
.img-slider__list {
grid-template-rows: 100%;
grid-template-columns: 100%;
place-items: center;
width: 100%;
height: 100%;
display: grid;
overflow: hidden;
}
.img-slide {
opacity: 0;
pointer-events: none;
will-change: transform, opacity;
grid-area: 1 / 1 / -1 / -1;
place-items: center;
width: 100%;
height: 100%;
display: grid;
position: relative;
overflow: hidden;
}
.img-slide.is--current {
opacity: 1;
pointer-events: auto;
}
.img-slide__inner {
object-fit: cover;
will-change: transform;
width: 100%;
height: 100%;
position: absolute;
}
.img-slider__nav {
z-index: 2;
grid-column-gap: .5rem;
grid-row-gap: .5rem;
pointer-events: none;
flex-flow: wrap;
justify-content: center;
align-items: center;
max-width: 95vw;
display: flex;
position: absolute;
bottom: 2rem;
}
.img-slider__thumb {
aspect-ratio: 1.5;
pointer-events: auto;
cursor: pointer;
border: 1px solid #fff3;
border-radius: .3125rem;
width: 7rem;
transition: border-color .2s;
position: relative;
overflow: hidden;
}
.img-slider__thumb:hover {
border-color: #fff6;
}
.img-slider__thumb.is--current {
border-color: #fff;
}
.slider-thumb__img {
object-fit: cover;
width: 100%;
height: 100%;
}
@media screen and (max-width: 991px) {
.img-slider__list {
width: 100%;
}
.img-slider__thumb {
flex: none;
}
}
@media screen and (max-width: 767px) {
.img-slider__nav {
flex-flow: wrap;
}
.img-slider__thumb {
border-radius: .25rem;
width: 5rem;
}
}
@media screen and (max-width: 479px) {
.img-slider__thumb {
width: 4.5rem;
}
}
Step 2: Add custom Javascript
Custom Javascript in Webflow
In this video, Ilja gives you some guidance about using JavaScript in Webflow:
Step 2: Add Javascript
Step 3: Add Javascript
Javascript
gsap.registerPlugin(Observer,CustomEase)
CustomEase.create("slideshow-wipe", "0.6, 0.08, 0.02, 0.99");
function initSlideShow(el) {
// Save all elements in an object for easy reference
const ui = {
el,
slides: Array.from(el.querySelectorAll('[data-slideshow="slide"]')),
inner: Array.from(el.querySelectorAll('[data-slideshow="parallax"]')),
thumbs: Array.from(el.querySelectorAll('[data-slideshow="thumb"]'))
};
let current = 0;
const length = ui.slides.length;
let animating = false;
let observer;
let animationDuration = 0.9 // Define the duration of your 'slide' here
ui.slides.forEach((slide, index) => {
slide.setAttribute('data-index', index);
});
ui.thumbs.forEach((thumb, index) => {
thumb.setAttribute('data-index', index);
});
ui.slides[current].classList.add('is--current');
ui.thumbs[current].classList.add('is--current');
function navigate(direction, targetIndex = null) {
if (animating) return;
animating = true;
observer.disable();
const previous = current;
current =
targetIndex !== null && targetIndex !== undefined
? targetIndex
: direction === 1
? current < length - 1
? current + 1
: 0
: current > 0
? current - 1
: length - 1;
const currentSlide = ui.slides[previous];
const currentInner = ui.inner[previous];
const upcomingSlide = ui.slides[current];
const upcomingInner = ui.inner[current];
gsap.timeline({
defaults: {
duration: animationDuration,
ease: 'slideshow-wipe'
},
onStart: function() {
upcomingSlide.classList.add('is--current');
ui.thumbs[previous].classList.remove('is--current');
ui.thumbs[current].classList.add('is--current');
},
onComplete: function() {
currentSlide.classList.remove('is--current');
animating = false;
// Re-enable observer after a short delay
setTimeout(() => observer.enable(), animationDuration);
}
})
.to(currentSlide, { xPercent: -direction * 100 },0)
.to(currentInner, { xPercent: direction * 50 }, 0)
.fromTo(upcomingSlide, { xPercent: direction * 100 }, { xPercent: 0 }, 0)
.fromTo(upcomingInner, { xPercent: -direction * 50 }, { xPercent: 0 }, 0);
}
function onClick(event) {
const targetIndex = parseInt(event.currentTarget.getAttribute('data-index'), 10);
if (targetIndex === current || animating) return;
const direction = targetIndex > current ? 1 : -1;
navigate(direction, targetIndex);
}
ui.thumbs.forEach(thumb => {
thumb.addEventListener('click', onClick);
});
observer = Observer.create({
target: el,
type: 'wheel,touch,pointer',
// Drag events to go left/right
onLeft: () => { if (!animating) navigate(1); },
onRight: () => {if (!animating) navigate(-1); },
// For wheel events, check horizontal movement
onWheel: (event) => {
if (animating) return;
if (Math.abs(event.deltaX) > Math.abs(event.deltaY)) {
if (event.deltaX > 50) {
navigate(1);
} else if (event.deltaX < -50) {
navigate(-1);
}
}
},
wheelSpeed: -1,
tolerance: 10
});
// Cleanup function if you need it
return {
destroy: function() {
if (observer) observer.kill();
ui.thumbs.forEach(thumb => {
thumb.removeEventListener('click', onClick);
});
}
};
}
document.addEventListener("DOMContentLoaded", () => {
let wrappers = document.querySelectorAll('[data-slideshow="wrap"]');
wrappers.forEach(wrap => initSlideShow(wrap));
});
Step 3: Add custom CSS
Step 2: Add custom CSS
Custom CSS in Webflow
Curious about where to put custom CSS in Webflow? Ilja explains it in the below video:
CSS
Implementation
This parallax slider uses GSAP animations and the Observer plugin to create smooth slide transitions triggered by click, wheel, and touch interactions. Here's how you can implement it yourself:
HTML Structure
Use the provided HTML structure:
- Wrap your slides and thumbnails inside a container with the attribute
data-slideshow="wrap"
. - Each slide requires
data-slideshow="slide"
, with the current active slide adding the classis--current
. - Parallax images within slides should have
data-slideshow="parallax"
. - Thumbnails should use
data-slideshow="thumb"
, again marking the active thumbnail withis--current
.
CSS Styling
The provided CSS manages layout, responsiveness, and transitions:
- Slides use CSS Grid to overlap fully, transitioning visibility via opacity.
- Thumbnails have hover effects and visual indicators for the active state.
JavaScript Functionality
The JavaScript initializes GSAP and the Observer plugin, handling interaction and animation:
- Initialization: Upon DOMContentLoaded, the slider initializes automatically.
- Animation: GSAP timelines animate slides horizontally with a parallax effect, using a custom ease.
- Interaction: Users navigate slides via:
- Clicking thumbnails to directly select slides.
- Horizontal scroll or drag gestures to transition slides smoothly.
- Observer Plugin: Manages intuitive navigation through wheel, touch, and pointer gestures.
Customization Tips
- Adjusting Speed: Modify
animationDuration
in the JavaScript to tweak transition speed. - Easing: Customize the
slideshow-wipe
ease for personalized animation.
Webflow and CMS Integration
To use this slider effectively within Webflow CMS:
- Create two separate CMS Collection Lists: one for the slides, and another for thumbnails.
- Apply the custom attributes (
data-slideshow="slide"
,data-slideshow="thumb"
, anddata-slideshow="parallax"
) directly to the CMS Collection List element (not the wrapper div) and to individual items to match the provided HTML structure. - Ensure that both CMS lists have the same number of items and are ordered consistently for synchronization.
Resource Details
Last updated
March 24, 2025
Type
The Vault
Category
Gallery & Images
Need help?
Join Slack