Account Settings
FIRST NAME
[Loading...]
LAST NAME
[Loading...]
EMAIL
[Loading...]
ID
[Loading...] For support purposes only
Share AgencyGenius

Step 1: Add HTML, Css & JavaScript

Code
Html
Css
Javascript
Combined
 <!-- 
 PARALLAX IMAGE SLIDER WITH THUMBNAILS 
 A vanilla JavaScript implementation of a parallax image slider with thumbnail navigation. 
 This version is optimized for mobile responsiveness while maintaining the original design. 
 --> 
 <div id="macos-parallax-slider-widget" class="img-slider"> 
 <div class="img-slider__list"> 
 <div data-slideshow="slide" class="img-slide is--current"> 
 <img data-slideshow="parallax" alt="macOS Sonoma Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-sonoma-wallpapers-5120x2160-v0-j9vwvbq8h5wb1.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="macOS Mojave Day Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macOS-Mojave-Day-wallpaper.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="macOS Abstract Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/da68i50-00fbde21-b0fa-49cc-a0ca-cdebca7b0730.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="macOS Big Sur Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-big-sur-ysfrml6k0il45zd8.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="Mountain Scenery macOS Wallpaper 1" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-106-0-h.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="Mountain Scenery macOS Wallpaper 2" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-108-0-h.jpg" 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://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-sonoma-wallpapers-5120x2160-v0-j9vwvbq8h5wb1.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macOS-Mojave-Day-wallpaper.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/da68i50-00fbde21-b0fa-49cc-a0ca-cdebca7b0730.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-big-sur-ysfrml6k0il45zd8.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-106-0-h.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-108-0-h.jpg" class="slider-thumb__img"> 
 </div> 
 </div> 
 </div> 
 <!-- End of PARALLAX IMAGE SLIDER WITH THUMBNAILS --> 
 /* Base slider styling */ 
 #macos-parallax-slider-widget { 
 --transition-duration: 900ms; 
 /* This custom bezier curve closely matches the GSAP "slideshow-wipe" custom ease */ 
 --transition-easing: cubic-bezier(0.6, 0.08, 0.02, 0.99); 
  
 grid-column-gap: 1rem; 
 grid-row-gap: 1rem; 
 border-radius: .5em; 
 justify-content: center; 
 align-items: flex-end; 
 width: 100%; 
 /* Use aspect ratio instead of fixed height for better responsiveness */ 
 aspect-ratio: 16/9; 
 max-height: 100vh; 
 display: flex; 
 position: relative; 
 } 
  
 #macos-parallax-slider-widget .img-slider__list { 
 grid-template-rows: 100%; 
 grid-template-columns: 100%; 
 place-items: center; 
 width: 100%; 
 height: 100%; 
 display: grid; 
 overflow: hidden; 
 position: relative; 
 } 
  
 #macos-parallax-slider-widget .img-slide { 
 opacity: 0; 
 pointer-events: none; 
 grid-area: 1 / 1 / -1 / -1; 
 place-items: center; 
 width: 100%; 
 height: 100%; 
 display: grid; 
 position: absolute; 
 top: 0; 
 left: 0; 
 overflow: hidden; 
 will-change: transform, opacity; 
 /* Start positioned off-screen */ 
 transform: translateX(100%); 
 z-index: 1; 
 } 
  
 #macos-parallax-slider-widget .img-slide.is--current { 
 opacity: 1; 
 pointer-events: auto; 
 transform: translateX(0); 
 z-index: 2; 
 } 
  
 #macos-parallax-slider-widget .img-slide.is--previous { 
 z-index: 1; 
 opacity: 1; 
 } 
  
 #macos-parallax-slider-widget .img-slide__inner { 
 object-fit: cover; 
 width: 100%; 
 height: 100%; 
 position: absolute; 
 will-change: transform; 
 } 
  
 #macos-parallax-slider-widget .img-slider__nav { 
 z-index: 10; 
 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; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 aspect-ratio: 1.5; 
 pointer-events: auto; 
 cursor: pointer; 
 border: 1px solid rgba(255, 255, 255, 0.2); 
 border-radius: .3125rem; 
 width: 7rem; 
 transition: border-color .2s; 
 position: relative; 
 overflow: hidden; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb:hover { 
 border-color: rgba(255, 255, 255, 0.4); 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb.is--current { 
 border-color: #fff; 
 } 
  
 #macos-parallax-slider-widget .slider-thumb__img { 
 object-fit: cover; 
 width: 100%; 
 height: 100%; 
 } 
  
 /* Auto-transition for slider */ 
 @keyframes slider-auto { 
 0%, 85% { opacity: 1; } 
 100% { opacity: 1; } 
 } 
  
 /* Responsive adjustments */ 
 @media screen and (max-width: 991px) { 
 #macos-parallax-slider-widget { 
 /* Slightly adjust aspect ratio for tablets */ 
 aspect-ratio: 4/3; 
 } 
  
 #macos-parallax-slider-widget .img-slider__list { 
 width: 100%; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 flex: none; 
 } 
 } 
  
 @media screen and (max-width: 767px) { 
 #macos-parallax-slider-widget { 
 /* More compact aspect ratio for mobile */ 
 aspect-ratio: 3/4; 
 } 
  
 #macos-parallax-slider-widget .img-slider__nav { 
 flex-flow: wrap; 
 /* Adjust bottom position on mobile */ 
 bottom: 1rem; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 border-radius: .25rem; 
 width: 5rem; 
 } 
 } 
  
 @media screen and (max-width: 479px) { 
 #macos-parallax-slider-widget .img-slider__thumb { 
 width: 4.5rem; 
 } 
 } 
  
 /* Handle landscape orientation on mobile */ 
 @media screen and (max-width: 767px) and (orientation: landscape) { 
 #macos-parallax-slider-widget { 
 /* Return to wider aspect ratio for landscape orientation */ 
 aspect-ratio: 16/9; 
 } 
  
 #macos-parallax-slider-widget .img-slider__nav { 
 bottom: 0.5rem; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 width: 4rem; 
 } 
 } 
 // Wait for DOM to be fully loaded 
 (function() { 
 // Immediately-invoked function to avoid polluting global namespace 
 function initParallaxSlider(sliderEl) { 
 // Animation settings 
 const ANIMATION_DURATION = 900; // Match CSS variable (in ms) 
 const AUTO_SLIDE_INTERVAL = 5000; // Time between auto slides (in ms) 
 let autoSlideTimer = null; 
 // Flag to track if animation is in progress 
 let animating = false; 
 // Store all slider elements 
 const ui = { 
 el: sliderEl, 
 container: sliderEl.querySelector('.img-slider__list'), 
 slides: Array.from(sliderEl.querySelectorAll('[data-slideshow="slide"]')), 
 inner: Array.from(sliderEl.querySelectorAll('[data-slideshow="parallax"]')), 
 thumbs: Array.from(sliderEl.querySelectorAll('[data-slideshow="thumb"]')) 
 }; 
 // Track current slide index 
 let current = 0; 
 const totalSlides = ui.slides.length; 
 // Add index attributes for easier reference 
 ui.slides.forEach((slide, index) => { 
 slide.setAttribute('data-index', index); 
 }); 
 ui.thumbs.forEach((thumb, index) => { 
 thumb.setAttribute('data-index', index); 
 }); 
 // Initialize the slider 
 function initialize() { 
 // Set initial slide 
 ui.slides[current].classList.add('is--current'); 
 ui.thumbs[current].classList.add('is--current'); 
 // Set up event listeners 
 setupThumbnailListeners(); 
 setupGestureListeners(); 
 // Start auto-sliding if more than one slide 
 if (ui.slides.length > 1) { 
 startAutoSlide(); 
 } 
 } 
 // Start auto-sliding functionality 
 function startAutoSlide() { 
 if (autoSlideTimer) { 
 clearInterval(autoSlideTimer); 
 } 
 autoSlideTimer = setInterval(() => { 
 if (!animating) { 
 navigate(1); 
 } 
 }, AUTO_SLIDE_INTERVAL); 
 } 
 // Pause auto-sliding (on user interaction) 
 function pauseAutoSlide() { 
 if (autoSlideTimer) { 
 clearInterval(autoSlideTimer); 
 autoSlideTimer = null; 
 } 
 } 
 // Resume auto-sliding (after user interaction) 
 function resumeAutoSlide() { 
 pauseAutoSlide(); 
 startAutoSlide(); 
 } 
 // Handle navigation between slides 
 function navigate(direction, targetIndex = null) { 
 if (animating) return; 
 animating = true; 
 // Pause auto-sliding during manual navigation 
 pauseAutoSlide(); 
 // Store previous slide index 
 const previous = current; 
 // Calculate new slide index 
 if (targetIndex !== null && targetIndex !== undefined) { 
 current = targetIndex; 
 direction = targetIndex > previous ? 1 : -1; 
 } else { 
 current = direction === 1 
 ? (current < totalSlides - 1 ? current + 1 : 0) 
 : (current > 0 ? current - 1 : totalSlides - 1); 
 } 
 // Get slide elements 
 const prevSlide = ui.slides[previous]; 
 const prevInner = ui.inner[previous]; 
 const nextSlide = ui.slides[current]; 
 const nextInner = ui.inner[current]; 
 // Mark previous slide 
 prevSlide.classList.add('is--previous'); 
 // Reset all slides not involved in transition 
 ui.slides.forEach((slide, idx) => { 
 if (idx !== previous && idx !== current) { 
 slide.style.transform = 'translateX(100%)'; 
 slide.classList.remove('is--current', 'is--previous'); 
 ui.inner[idx].style.transform = ''; 
 } 
 }); 
 // Animation steps using requestAnimationFrame for smoother transitions 
 // Step 1: Setup starting positions 
 if (direction === 1) { 
 // Going forward: current slide moves left, next slide comes from right 
 nextSlide.style.transform = 'translateX(100%)'; 
 nextInner.style.transform = 'translateX(-50%)'; 
 } else { 
 // Going backward: current slide moves right, next slide comes from left 
 nextSlide.style.transform = 'translateX(-100%)'; 
 nextInner.style.transform = 'translateX(50%)'; 
 } 
 // Update thumbnail indicators immediately 
 ui.thumbs[previous].classList.remove('is--current'); 
 ui.thumbs[current].classList.add('is--current'); 
 // Step 2: Apply transitions 
 requestAnimationFrame(() => { 
 // Apply transition styles 
 applyTransitionStyles(prevSlide); 
 applyTransitionStyles(prevInner); 
 applyTransitionStyles(nextSlide); 
 applyTransitionStyles(nextInner); 
 // Force a reflow to ensure transitions take effect 
 void nextSlide.offsetWidth; 
 // Step 3: Move to final positions 
 requestAnimationFrame(() => { 
 // Move the slides to their final positions 
 if (direction === 1) { 
 prevSlide.style.transform = 'translateX(-100%)'; 
 prevInner.style.transform = 'translateX(50%)'; 
 } else { 
 prevSlide.style.transform = 'translateX(100%)'; 
 prevInner.style.transform = 'translateX(-50%)'; 
 } 
 // Bring new slide into view 
 nextSlide.style.transform = 'translateX(0)'; 
 nextInner.style.transform = 'translateX(0)'; 
 // Add current class to make it visible 
 nextSlide.classList.add('is--current'); 
 // Handle cleanup after transition completes 
 setTimeout(() => { 
 // Remove transition styles 
 removeTransitionStyles(prevSlide); 
 removeTransitionStyles(prevInner); 
 removeTransitionStyles(nextSlide); 
 removeTransitionStyles(nextInner); 
 // Reset previous slide 
 prevSlide.classList.remove('is--current', 'is--previous'); 
 // Re-enable interactions 
 animating = false; 
 // Resume auto-sliding 
 resumeAutoSlide(); 
 }, ANIMATION_DURATION); 
 }); 
 }); 
 } 
 // Apply transition styles to an element 
 function applyTransitionStyles(element) { 
 element.style.transition = `transform ${ANIMATION_DURATION}ms var(--transition-easing)`; 
 } 
 // Remove transition styles from an element 
 function removeTransitionStyles(element) { 
 element.style.transition = ''; 
 } 
 // Set up click handlers for thumbnails 
 function setupThumbnailListeners() { 
 ui.thumbs.forEach(thumb => { 
 thumb.addEventListener('click', handleThumbClick); 
 }); 
 } 
 // Handle thumbnail click 
 function handleThumbClick(event) { 
 const targetIndex = parseInt(event.currentTarget.getAttribute('data-index'), 10); 
 if (targetIndex === current || animating) return; 
 const direction = targetIndex > current ? 1 : -1; 
 navigate(direction, targetIndex); 
 } 
 // Touch and wheel gesture handling 
 function setupGestureListeners() { 
 // Variables for touch tracking 
 let touchStartX = 0; 
 let touchEndX = 0; 
 const minSwipeDistance = 50; 
 // Pause auto-sliding on user interaction 
 sliderEl.addEventListener('mouseenter', pauseAutoSlide); 
 sliderEl.addEventListener('mouseleave', resumeAutoSlide); 
 sliderEl.addEventListener('touchstart', pauseAutoSlide, { passive: true }); 
 // Touch events for mobile 
 sliderEl.addEventListener('touchstart', function(e) { 
 touchStartX = e.changedTouches[0].screenX; 
 }, { passive: true }); 
 sliderEl.addEventListener('touchend', function(e) { 
 if (animating) return; 
 touchEndX = e.changedTouches[0].screenX; 
 handleSwipe(); 
 // Resume auto-sliding after touch 
 setTimeout(resumeAutoSlide, 500); 
 }, { passive: true }); 
 // Mouse wheel events 
 sliderEl.addEventListener('wheel', function(e) { 
 if (animating) return; 
 // Only respond to horizontal wheel events 
 if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) { 
 // Prevent page scrolling 
 e.preventDefault(); 
 if (e.deltaX > minSwipeDistance) { 
 navigate(1); 
 } else if (e.deltaX < -minSwipeDistance) { 
 navigate(-1); 
 } 
 } 
 }, { passive: false }); 
 // Process swipe direction 
 function handleSwipe() { 
 const swipeDistance = touchEndX - touchStartX; 
 if (Math.abs(swipeDistance) > minSwipeDistance) { 
 if (swipeDistance < 0) { 
 // Swipe left, go to next 
 navigate(1); 
 } else { 
 // Swipe right, go to previous 
 navigate(-1); 
 } 
 } 
 } 
 } 
 // Initialize the slider 
 initialize(); 
 // Return public methods for external control 
 return { 
 goTo: function(index) { 
 if (index >= 0 && index < totalSlides && index !== current) { 
 const direction = index > current ? 1 : -1; 
 navigate(direction, index); 
 } 
 }, 
 next: function() { 
 navigate(1); 
 }, 
 prev: function() { 
 navigate(-1); 
 }, 
 pause: pauseAutoSlide, 
 resume: resumeAutoSlide 
 }; 
 } 
 // Initialize all sliders when DOM is ready 
 document.addEventListener("DOMContentLoaded", function() { 
 const sliders = document.querySelectorAll('#macos-parallax-slider-widget'); 
 sliders.forEach(slider => { 
 const sliderInstance = initParallaxSlider(slider); 
 // Store the slider instance on the element for external access 
 slider.sliderAPI = sliderInstance; 
 }); 
 }); 
 })(); 
 <div id="snippet-o2yykqou"> 
 <!-- HTML SECTION START --> 
 <!-- 
 PARALLAX IMAGE SLIDER WITH THUMBNAILS 
 A vanilla JavaScript implementation of a parallax image slider with thumbnail navigation. 
 This version is optimized for mobile responsiveness while maintaining the original design. 
 --> 
 <div id="macos-parallax-slider-widget" class="img-slider"> 
 <div class="img-slider__list"> 
 <div data-slideshow="slide" class="img-slide is--current"> 
 <img data-slideshow="parallax" alt="macOS Sonoma Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-sonoma-wallpapers-5120x2160-v0-j9vwvbq8h5wb1.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="macOS Mojave Day Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macOS-Mojave-Day-wallpaper.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="macOS Abstract Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/da68i50-00fbde21-b0fa-49cc-a0ca-cdebca7b0730.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="macOS Big Sur Wallpaper" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-big-sur-ysfrml6k0il45zd8.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="Mountain Scenery macOS Wallpaper 1" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-106-0-h.jpg" draggable="false" class="img-slide__inner"> 
 </div> 
 <div data-slideshow="slide" class="img-slide"> 
 <img data-slideshow="parallax" alt="Mountain Scenery macOS Wallpaper 2" src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-108-0-h.jpg" 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://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-sonoma-wallpapers-5120x2160-v0-j9vwvbq8h5wb1.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macOS-Mojave-Day-wallpaper.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/da68i50-00fbde21-b0fa-49cc-a0ca-cdebca7b0730.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/macos-big-sur-ysfrml6k0il45zd8.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-106-0-h.jpg" class="slider-thumb__img"> 
 </div> 
 <div data-slideshow="thumb" class="img-slider__thumb"> 
 <img src="https://irp.cdn-website.com/a778beb9/dms3rep/multi/mountain-scenery-macos-4k-wallpaper-uhdpaper.com-108-0-h.jpg" class="slider-thumb__img"> 
 </div> 
 </div> 
 </div> 
 <!-- End of PARALLAX IMAGE SLIDER WITH THUMBNAILS --> 
 <!-- HTML SECTION END --> 
  
 <!-- CSS SECTION START --> 
 <style> 
 /* Base slider styling */ 
 #macos-parallax-slider-widget { 
 --transition-duration: 900ms; 
 /* This custom bezier curve closely matches the GSAP "slideshow-wipe" custom ease */ 
 --transition-easing: cubic-bezier(0.6, 0.08, 0.02, 0.99); 
  
 grid-column-gap: 1rem; 
 grid-row-gap: 1rem; 
 border-radius: .5em; 
 justify-content: center; 
 align-items: flex-end; 
 width: 100%; 
 /* Use aspect ratio instead of fixed height for better responsiveness */ 
 aspect-ratio: 16/9; 
 max-height: 100vh; 
 display: flex; 
 position: relative; 
 } 
  
 #macos-parallax-slider-widget .img-slider__list { 
 grid-template-rows: 100%; 
 grid-template-columns: 100%; 
 place-items: center; 
 width: 100%; 
 height: 100%; 
 display: grid; 
 overflow: hidden; 
 position: relative; 
 } 
  
 #macos-parallax-slider-widget .img-slide { 
 opacity: 0; 
 pointer-events: none; 
 grid-area: 1 / 1 / -1 / -1; 
 place-items: center; 
 width: 100%; 
 height: 100%; 
 display: grid; 
 position: absolute; 
 top: 0; 
 left: 0; 
 overflow: hidden; 
 will-change: transform, opacity; 
 /* Start positioned off-screen */ 
 transform: translateX(100%); 
 z-index: 1; 
 } 
  
 #macos-parallax-slider-widget .img-slide.is--current { 
 opacity: 1; 
 pointer-events: auto; 
 transform: translateX(0); 
 z-index: 2; 
 } 
  
 #macos-parallax-slider-widget .img-slide.is--previous { 
 z-index: 1; 
 opacity: 1; 
 } 
  
 #macos-parallax-slider-widget .img-slide__inner { 
 object-fit: cover; 
 width: 100%; 
 height: 100%; 
 position: absolute; 
 will-change: transform; 
 } 
  
 #macos-parallax-slider-widget .img-slider__nav { 
 z-index: 10; 
 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; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 aspect-ratio: 1.5; 
 pointer-events: auto; 
 cursor: pointer; 
 border: 1px solid rgba(255, 255, 255, 0.2); 
 border-radius: .3125rem; 
 width: 7rem; 
 transition: border-color .2s; 
 position: relative; 
 overflow: hidden; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb:hover { 
 border-color: rgba(255, 255, 255, 0.4); 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb.is--current { 
 border-color: #fff; 
 } 
  
 #macos-parallax-slider-widget .slider-thumb__img { 
 object-fit: cover; 
 width: 100%; 
 height: 100%; 
 } 
  
 /* Auto-transition for slider */ 
 @keyframes slider-auto { 
 0%, 85% { opacity: 1; } 
 100% { opacity: 1; } 
 } 
  
 /* Responsive adjustments */ 
 @media screen and (max-width: 991px) { 
 #macos-parallax-slider-widget { 
 /* Slightly adjust aspect ratio for tablets */ 
 aspect-ratio: 4/3; 
 } 
  
 #macos-parallax-slider-widget .img-slider__list { 
 width: 100%; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 flex: none; 
 } 
 } 
  
 @media screen and (max-width: 767px) { 
 #macos-parallax-slider-widget { 
 /* More compact aspect ratio for mobile */ 
 aspect-ratio: 3/4; 
 } 
  
 #macos-parallax-slider-widget .img-slider__nav { 
 flex-flow: wrap; 
 /* Adjust bottom position on mobile */ 
 bottom: 1rem; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 border-radius: .25rem; 
 width: 5rem; 
 } 
 } 
  
 @media screen and (max-width: 479px) { 
 #macos-parallax-slider-widget .img-slider__thumb { 
 width: 4.5rem; 
 } 
 } 
  
 /* Handle landscape orientation on mobile */ 
 @media screen and (max-width: 767px) and (orientation: landscape) { 
 #macos-parallax-slider-widget { 
 /* Return to wider aspect ratio for landscape orientation */ 
 aspect-ratio: 16/9; 
 } 
  
 #macos-parallax-slider-widget .img-slider__nav { 
 bottom: 0.5rem; 
 } 
  
 #macos-parallax-slider-widget .img-slider__thumb { 
 width: 4rem; 
 } 
 } 
 </style> 
 <!-- CSS SECTION END --> 
  
 <!-- JAVASCRIPT SECTION START --> 
 <script> 
 // Wait for DOM to be fully loaded 
 (function() { 
 // Immediately-invoked function to avoid polluting global namespace 
 function initParallaxSlider(sliderEl) { 
 // Animation settings 
 const ANIMATION_DURATION = 900; // Match CSS variable (in ms) 
 const AUTO_SLIDE_INTERVAL = 5000; // Time between auto slides (in ms) 
 let autoSlideTimer = null; 
 // Flag to track if animation is in progress 
 let animating = false; 
 // Store all slider elements 
 const ui = { 
 el: sliderEl, 
 container: sliderEl.querySelector('.img-slider__list'), 
 slides: Array.from(sliderEl.querySelectorAll('[data-slideshow="slide"]')), 
 inner: Array.from(sliderEl.querySelectorAll('[data-slideshow="parallax"]')), 
 thumbs: Array.from(sliderEl.querySelectorAll('[data-slideshow="thumb"]')) 
 }; 
 // Track current slide index 
 let current = 0; 
 const totalSlides = ui.slides.length; 
 // Add index attributes for easier reference 
 ui.slides.forEach((slide, index) => { 
 slide.setAttribute('data-index', index); 
 }); 
 ui.thumbs.forEach((thumb, index) => { 
 thumb.setAttribute('data-index', index); 
 }); 
 // Initialize the slider 
 function initialize() { 
 // Set initial slide 
 ui.slides[current].classList.add('is--current'); 
 ui.thumbs[current].classList.add('is--current'); 
 // Set up event listeners 
 setupThumbnailListeners(); 
 setupGestureListeners(); 
 // Start auto-sliding if more than one slide 
 if (ui.slides.length > 1) { 
 startAutoSlide(); 
 } 
 } 
 // Start auto-sliding functionality 
 function startAutoSlide() { 
 if (autoSlideTimer) { 
 clearInterval(autoSlideTimer); 
 } 
 autoSlideTimer = setInterval(() => { 
 if (!animating) { 
 navigate(1); 
 } 
 }, AUTO_SLIDE_INTERVAL); 
 } 
 // Pause auto-sliding (on user interaction) 
 function pauseAutoSlide() { 
 if (autoSlideTimer) { 
 clearInterval(autoSlideTimer); 
 autoSlideTimer = null; 
 } 
 } 
 // Resume auto-sliding (after user interaction) 
 function resumeAutoSlide() { 
 pauseAutoSlide(); 
 startAutoSlide(); 
 } 
 // Handle navigation between slides 
 function navigate(direction, targetIndex = null) { 
 if (animating) return; 
 animating = true; 
 // Pause auto-sliding during manual navigation 
 pauseAutoSlide(); 
 // Store previous slide index 
 const previous = current; 
 // Calculate new slide index 
 if (targetIndex !== null && targetIndex !== undefined) { 
 current = targetIndex; 
 direction = targetIndex > previous ? 1 : -1; 
 } else { 
 current = direction === 1 
 ? (current < totalSlides - 1 ? current + 1 : 0) 
 : (current > 0 ? current - 1 : totalSlides - 1); 
 } 
 // Get slide elements 
 const prevSlide = ui.slides[previous]; 
 const prevInner = ui.inner[previous]; 
 const nextSlide = ui.slides[current]; 
 const nextInner = ui.inner[current]; 
 // Mark previous slide 
 prevSlide.classList.add('is--previous'); 
 // Reset all slides not involved in transition 
 ui.slides.forEach((slide, idx) => { 
 if (idx !== previous && idx !== current) { 
 slide.style.transform = 'translateX(100%)'; 
 slide.classList.remove('is--current', 'is--previous'); 
 ui.inner[idx].style.transform = ''; 
 } 
 }); 
 // Animation steps using requestAnimationFrame for smoother transitions 
 // Step 1: Setup starting positions 
 if (direction === 1) { 
 // Going forward: current slide moves left, next slide comes from right 
 nextSlide.style.transform = 'translateX(100%)'; 
 nextInner.style.transform = 'translateX(-50%)'; 
 } else { 
 // Going backward: current slide moves right, next slide comes from left 
 nextSlide.style.transform = 'translateX(-100%)'; 
 nextInner.style.transform = 'translateX(50%)'; 
 } 
 // Update thumbnail indicators immediately 
 ui.thumbs[previous].classList.remove('is--current'); 
 ui.thumbs[current].classList.add('is--current'); 
 // Step 2: Apply transitions 
 requestAnimationFrame(() => { 
 // Apply transition styles 
 applyTransitionStyles(prevSlide); 
 applyTransitionStyles(prevInner); 
 applyTransitionStyles(nextSlide); 
 applyTransitionStyles(nextInner); 
 // Force a reflow to ensure transitions take effect 
 void nextSlide.offsetWidth; 
 // Step 3: Move to final positions 
 requestAnimationFrame(() => { 
 // Move the slides to their final positions 
 if (direction === 1) { 
 prevSlide.style.transform = 'translateX(-100%)'; 
 prevInner.style.transform = 'translateX(50%)'; 
 } else { 
 prevSlide.style.transform = 'translateX(100%)'; 
 prevInner.style.transform = 'translateX(-50%)'; 
 } 
 // Bring new slide into view 
 nextSlide.style.transform = 'translateX(0)'; 
 nextInner.style.transform = 'translateX(0)'; 
 // Add current class to make it visible 
 nextSlide.classList.add('is--current'); 
 // Handle cleanup after transition completes 
 setTimeout(() => { 
 // Remove transition styles 
 removeTransitionStyles(prevSlide); 
 removeTransitionStyles(prevInner); 
 removeTransitionStyles(nextSlide); 
 removeTransitionStyles(nextInner); 
 // Reset previous slide 
 prevSlide.classList.remove('is--current', 'is--previous'); 
 // Re-enable interactions 
 animating = false; 
 // Resume auto-sliding 
 resumeAutoSlide(); 
 }, ANIMATION_DURATION); 
 }); 
 }); 
 } 
 // Apply transition styles to an element 
 function applyTransitionStyles(element) { 
 element.style.transition = `transform ${ANIMATION_DURATION}ms var(--transition-easing)`; 
 } 
 // Remove transition styles from an element 
 function removeTransitionStyles(element) { 
 element.style.transition = ''; 
 } 
 // Set up click handlers for thumbnails 
 function setupThumbnailListeners() { 
 ui.thumbs.forEach(thumb => { 
 thumb.addEventListener('click', handleThumbClick); 
 }); 
 } 
 // Handle thumbnail click 
 function handleThumbClick(event) { 
 const targetIndex = parseInt(event.currentTarget.getAttribute('data-index'), 10); 
 if (targetIndex === current || animating) return; 
 const direction = targetIndex > current ? 1 : -1; 
 navigate(direction, targetIndex); 
 } 
 // Touch and wheel gesture handling 
 function setupGestureListeners() { 
 // Variables for touch tracking 
 let touchStartX = 0; 
 let touchEndX = 0; 
 const minSwipeDistance = 50; 
 // Pause auto-sliding on user interaction 
 sliderEl.addEventListener('mouseenter', pauseAutoSlide); 
 sliderEl.addEventListener('mouseleave', resumeAutoSlide); 
 sliderEl.addEventListener('touchstart', pauseAutoSlide, { passive: true }); 
 // Touch events for mobile 
 sliderEl.addEventListener('touchstart', function(e) { 
 touchStartX = e.changedTouches[0].screenX; 
 }, { passive: true }); 
 sliderEl.addEventListener('touchend', function(e) { 
 if (animating) return; 
 touchEndX = e.changedTouches[0].screenX; 
 handleSwipe(); 
 // Resume auto-sliding after touch 
 setTimeout(resumeAutoSlide, 500); 
 }, { passive: true }); 
 // Mouse wheel events 
 sliderEl.addEventListener('wheel', function(e) { 
 if (animating) return; 
 // Only respond to horizontal wheel events 
 if (Math.abs(e.deltaX) > Math.abs(e.deltaY)) { 
 // Prevent page scrolling 
 e.preventDefault(); 
 if (e.deltaX > minSwipeDistance) { 
 navigate(1); 
 } else if (e.deltaX < -minSwipeDistance) { 
 navigate(-1); 
 } 
 } 
 }, { passive: false }); 
 // Process swipe direction 
 function handleSwipe() { 
 const swipeDistance = touchEndX - touchStartX; 
 if (Math.abs(swipeDistance) > minSwipeDistance) { 
 if (swipeDistance < 0) { 
 // Swipe left, go to next 
 navigate(1); 
 } else { 
 // Swipe right, go to previous 
 navigate(-1); 
 } 
 } 
 } 
 } 
 // Initialize the slider 
 initialize(); 
 // Return public methods for external control 
 return { 
 goTo: function(index) { 
 if (index >= 0 && index < totalSlides && index !== current) { 
 const direction = index > current ? 1 : -1; 
 navigate(direction, index); 
 } 
 }, 
 next: function() { 
 navigate(1); 
 }, 
 prev: function() { 
 navigate(-1); 
 }, 
 pause: pauseAutoSlide, 
 resume: resumeAutoSlide 
 }; 
 } 
 // Initialize all sliders when DOM is ready 
 document.addEventListener("DOMContentLoaded", function() { 
 const sliders = document.querySelectorAll('#macos-parallax-slider-widget'); 
 sliders.forEach(slider => { 
 const sliderInstance = initParallaxSlider(slider); 
 // Store the slider instance on the element for external access 
 slider.sliderAPI = sliderInstance; 
 }); 
 }); 
 })(); 
 </script> 
 <!-- JAVASCRIPT SECTION END --> 
 </div> 

Implementation

The Parallax Image Slider creates an elegant, responsive gallery with a subtle parallax effect that enhances visual depth during transitions. When users navigate between slides, images shift slightly in the opposite direction of the transition, creating a sophisticated 3D-like effect. The widget features thumbnail navigation and works seamlessly across all device sizes.

HTML Structure

Use the following HTML structure:

  • The main container img-slider holds all slider elements
  • The img-slider__list contains all slide panels
  • Each slide is a img-slide div containing a parallax image
  • The first slide should have the is--current class applied initially
  • Images with the data-slideshow="parallax" attribute enable the parallax effect
  • Thumbnail navigation img-slider__nav contains small previews of each slide
  • Each thumbnail is a img-slider__thumb div with matching image
CSS Styling

The CSS manages layout, transitions, and responsive behavior:

  • Uses CSS variables for consistent settings:
    • --transition-duration controls animation speed
    • --transition-easing defines the custom timing function
  • Employs aspect-ratio instead of fixed height for better mobile responsiveness
  • Implements responsive breakpoints for different devices:
    • Desktop: 16:9 aspect ratio with larger thumbnails
    • Tablets (max-width: 991px): 4:3 aspect ratio
    • Mobile portrait (max-width: 767px): 3:4 aspect ratio with smaller thumbnails
    • Mobile landscape: returns to 16:9 aspect ratio with adjusted controls
  • Uses CSS grid for slide positioning:
    • grid-area: 1 / 1 / -1 / -1 stacks all slides in the same grid cell
    • position: absolute on slides enables transitions
  • Employs will-change: transform, opacity for performance optimization
JavaScript Functionality

The JavaScript handles all interactions and animations:

  • Initialization: Sets up event listeners and starts autoplay
  • Navigation: Handles transitions between slides with the parallax effect:
    • For next/forward: Current slide moves left, inner image moves right
    • For previous/backward: Current slide moves right, inner image moves left
  • Animation Control:
    • Uses requestAnimationFrame for smoother transitions
    • Applies transition styles dynamically using applyTransitionStyles()
    • Prevents multiple animations with the animating flag
  • Touch Gesture Support:
    • Detects swipe direction using touchstart and touchend events
    • Handles horizontal scrolling with wheel event detection
  • Autoplay:
    • Automatically transitions slides at regular intervals
    • Pauses on user interaction and resumes after a delay
Customization Tips

You can easily customize various aspects of the slider:

  • Transition speed: Modify the --transition-duration CSS variable (default: 900ms)
  • Easing curve: Adjust the --transition-easing value for different motion feels
  • Autoplay timing: Change the AUTO_SLIDE_INTERVAL constant (default: 5000ms)
  • Aspect ratios: Modify the aspect-ratio property in media queries for different proportions
  • Thumbnail size: Adjust the width value for .img-slider__thumb
  • Responsive breakpoints: Change the max-width values in media queries
  • Parallax intensity: Alter the translateX percentage values in the navigate() function
CMS Integration

To integrate with a CMS:

  • Create a collection for slider items with fields for:
    • Slide image (high-resolution for the main slide)
    • Thumbnail image (can be same as slide image but will be resized)
    • Alt text for accessibility
  • Implementation steps:
    • Loop through your collection to generate both slides and thumbnails
    • Ensure matching indexes between slides and thumbnails
    • Add is--current class to the first slide and thumbnail
    • Make sure to add proper data-slideshow attributes
  • For multiple sliders on one page:
    • Use unique IDs instead of #macos-parallax-slider-widget
    • Initialize each slider separately in the JavaScript
    • Use the returned API methods ( next() , prev() , goTo() ) to control each instance
Video Thumbnail

How to edit code using AI

Resource Details:
The Parallax Image Slider Widget creates a full-screen image slider with smooth parallax transitions between slides. It features thumbnail navigation, touch/swipe support, and auto-advancing slides. The parallax effect adds depth as images move at different speeds during transitions, all without requiring external libraries like GSAP.

Last Updated:

April 12th 2025

Category:

Image Display

Need Help?

Join Slack

Callum Wells
Callum Wells
i
Creator Credits
We always strive to credit creators as accurately as possible. While similar concepts might appear online, we aim to provide proper and respectful attribution.
A black padlock with a keyhole on a white background.

NO-ACCESS

Only for Desktop Users

This page is only available on desktop.

Please Log in on a desktop to gain access.