Parallax Carousel
A fully custom 3D parallax carousel built in vanilla JavaScript. Slides support drag-and-throw interaction, keyboard navigation, auto-play timeline, and responsive breakpoints, with layered parallax motion on both the card content and background.
- Custom carousel class with drag, inertia & auto-cycle
- Perspective-based parallax layers on card content & background
- Accessible pagination dots with ARIA labels
<!-- Parallax carousel root -->
<div class="mzaCarousel"
id="mzaCarousel"
aria-roledescription="carousel"
aria-label="Featured cards">
<div class="mzaCarousel-viewport" tabindex="0">
<div class="mzaCarousel-track">
<!-- Slide 1 -->
<article class="mzaCarousel-slide">
<div class="mzaCard"
style="--mzaCard-bg:url('https://picsum.photos/id/1015/1600/1000');">
<header class="mzaCard-head mzaPar-1">
<h2 class="mzaCard-title">Edge Visuals</h2>
</header>
...
</div>
</article>
<!-- more slides... -->
</div>
</div>
<!-- Prev / Next buttons + dots -->
<div class="mzaCarousel-controls">...</div>
</div>
:root {
--mzaC-fg: #e7ecf2;
--mzaC-accent: #9ef7d2;
--mzaC-accent2: #82a0ff;
...
}
.mzaCarousel {
position: relative;
height: 100vh;
perspective: 1200px;
...
}
.mzaCard {
backdrop-filter: saturate(120%) blur(4px);
box-shadow: 0 20px 50px rgba(0,0,0,.45);
...
}
// Parallax carousel controller
class MzaCarousel {
constructor(root, opts = {}) {
this.root = root;
this.viewport = root.querySelector(".mzaCarousel-viewport");
this.slides = [...root.querySelectorAll(".mzaCarousel-slide")];
...
}
_onDragStart(e) { ... }
_onDragMove(e) { ... }
_onDragEnd(e) { ... }
_startCycle() { ... }
_loop() { ... }
_render(markActive = false) {
const span = this.slideW + this.state.gap;
...
}
}
window.addEventListener("DOMContentLoaded", () => {
const root = document.getElementById("mzaCarousel");
if (!root) return;
new MzaCarousel(root, { transitionMs: 900 });
});