The CSS scroll-snap version covers most needs โ build THIS version to understand what libraries do: state + transform.
The engine
<div class="slider">
<div class="track">
<img src="1.jpg"><img src="2.jpg"><img src="3.jpg">
</div>
<button class="prev">โน</button><button class="next">โบ</button>
<div class="dots"></div>
</div>.slider { overflow: hidden; position: relative; }
.track { display: flex; transition: transform .4s ease; }
.track img { flex: 0 0 100%; }let index = 0;
const count = track.children.length;
function go(i) {
index = (i + count) % count; // wraps both directions
track.style.transform = `translateX(-${index * 100}%)`;
dots.forEach((d, j) => d.classList.toggle("active", j === index));
}
nextBtn.onclick = () => go(index + 1);
prevBtn.onclick = () => go(index - 1);
// autoplay + pause on hover
let auto = setInterval(() => go(index + 1), 4000);
slider.onmouseenter = () => clearInterval(auto);
slider.onmouseleave = () => auto = setInterval(() => go(index + 1), 4000);Touch swipe (the mobile 20%)
let startX = 0;
track.addEventListener("touchstart", (e) => startX = e.touches[0].clientX);
track.addEventListener("touchend", (e) => {
const dx = e.changedTouches[0].clientX - startX;
if (Math.abs(dx) > 50) go(index + (dx < 0 ? 1 : -1));
});Accessibility: aria-label on buttons, pause autoplay when prefers-reduced-motion.