webdevpuneet.com

Dot Pagination Carousel — Free HTML CSS JS Slide Carousel Snippet

Dot Pagination Carousel · Carousels · Plain HTML, CSS & JS · Live preview

Copy & paste

Source Code

<div class="carousel" id="carousel">
  <div class="carousel-track" id="carouselTrack">
    <div class="slide slide-1"><span>Slide 1</span></div>
    <div class="slide slide-2"><span>Slide 2</span></div>
    <div class="slide slide-3"><span>Slide 3</span></div>
    <div class="slide slide-4"><span>Slide 4</span></div>
  </div>
  <button class="arrow arrow-prev" onclick="moveSlide(-1)" aria-label="Previous slide">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>
  </button>
  <button class="arrow arrow-next" onclick="moveSlide(1)" aria-label="Next slide">
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2.5"><path d="M9 18l6-6-6-6"/></svg>
  </button>
  <div class="dots" id="dots" role="tablist" aria-label="Slide navigation">
    <button class="dot active" onclick="goToSlide(0)" aria-label="Go to slide 1"></button>
    <button class="dot" onclick="goToSlide(1)" aria-label="Go to slide 2"></button>
    <button class="dot" onclick="goToSlide(2)" aria-label="Go to slide 3"></button>
    <button class="dot" onclick="goToSlide(3)" aria-label="Go to slide 4"></button>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Single translateX transform on a flex track — no per-slide positioning logic needed
One currentIndex variable and one renderSlide function keep dots, arrows, and track perfectly in sync
Modulo-based wraparound lets prev/next arrows cycle seamlessly past the first and last slide
Active dot grows into a pill shape rather than just changing color, for a clearer active state
Dots and arrows are fully keyboard-clickable native <button> elements
role="tablist" and descriptive aria-labels on dots and arrows for screen reader support
Smooth 0.4s eased slide transition defined entirely in CSS
Adding or removing slides requires no changes to the JavaScript logic

About this UI Snippet

Dot Pagination Carousel — HTML, CSS & JavaScript Slide Carousel

Screenshot of the Dot Pagination Carousel snippet rendered live

Carousels are everywhere — hero banners, testimonial rotators, onboarding screens — and the dot-indicator-plus-arrows combination is the most recognizable navigation pattern for them. This snippet builds a complete carousel using a single sliding track and a row of clickable dots, with the currently active dot rendered as a wider pill rather than just a differently colored circle.

How the sliding mechanism works

All slides sit side by side inside .carousel-track with display: flex, each slide set to min-width: 100% so exactly one slide fills the visible .carousel viewport at a time (overflow: hidden on the parent hides the rest). Moving between slides is a single CSS transform: track.style.transform = 'translateX(-' + (currentIndex * 100) + '%)'. To show slide index 2, the track shifts left by exactly 200% of its own width — since each slide is 100% of the viewport, sliding by a multiple of 100% always lines up a slide exactly with the viewport. The transition: transform 0.4s ease on the track is what makes that jump animate smoothly instead of snapping.

How the dots and arrows share one state

There is exactly one source of truth: the currentIndex variable. Both goToSlide(index) (called by clicking a dot) and moveSlide(delta) (called by clicking an arrow, passing +1 or -1) do nothing but update currentIndex and then call the single renderSlide() function, which both moves the track and updates which dot has the .active class. This means dots and arrows can never disagree about which slide is showing — they're just two different ways of changing the same variable.

How the arrows wrap around

moveSlide computes (currentIndex + delta + totalSlides) % totalSlides. Adding totalSlides before the modulo is what correctly handles the "previous" click from slide 0 — without it, (0 - 1) % 4 would evaluate to -1 in JavaScript (a negative number, not a valid array-style wraparound), rather than the desired 3.

How the active dot pill effect works

The .dot.active class doesn't just change background — it also grows width from 8px to 22px and rounds the corners slightly less (border-radius: 4px instead of 50%), turning a circle into a short pill. This is a purely cosmetic CSS transition layered on top of the same class toggle used for the color change.

Step by step

How to Use

  1. 1
    Load the snippetClick "Dot Pagination Carousel" in the sidebar Library tab to load the four-slide demo.
  2. 2
    Click dots and arrowsClick any dot to jump directly to that slide, or use the prev/next arrows in the preview.
  3. 3
    Add real contentReplace each .slide div's background gradient with a real image via background-image or an <img> tag.
  4. 4
    Add more slidesAdd a new .slide div in the track and a matching dot button — totalSlides and the wraparound math update automatically.
  5. 5
    Add autoplayWrap moveSlide(1) in a setInterval to auto-advance, and clear it on user interaction if you want to pause on hover.
  6. 6
    Export and saveExport as HTML/JSX/Tailwind, or click "Save as" to reuse this carousel in a real hero or testimonial section.

Real-world uses

Common Use Cases

HERO
Hero banner carousels
Rotate through promotional banners or featured content on a homepage with clear dot navigation.
Onboarding and feature tour screens
Use as the base pattern for a multi-step onboarding flow where dots double as progress indicators.
REVIEW
Testimonial and review rotators
Cycle through customer testimonials or case study highlights with a familiar, accessible navigation pattern.
SHOP
Product image galleries
Adapt the same carousel to cycle through multiple images of a single product on a detail page.
Learn transform-based slide animation
Study why translateX percentage math naturally handles any number of equal-width slides without per-slide calculations.
Foundation for autoplay carousels
Extend with a setInterval calling moveSlide(1) to build an auto-advancing carousel, pausing it on hover or focus.
Related: Filter Tabs Gallery
See the Filter Tabs Gallery for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

Each slide is exactly 100% of the visible viewport width. Sliding to index N means shifting the track left by N times 100%, which the transform property does directly: translateX(-N00%). Because every slide is the same width, this math works for any number of slides without per-slide coordinates.

Both interactions only ever change one shared variable, currentIndex, and then call the same renderSlide function, which updates both the track position and the active dot. There is no separate state for "which dot is active" versus "which slide is showing" — they are driven by the same source of truth.

moveSlide computes (currentIndex + delta + totalSlides) % totalSlides. Adding totalSlides before the modulo operation ensures a negative intermediate value (like -1 when going back from slide 0) wraps around to the correct last-slide index instead of producing a negative number.

Wrap a call to moveSlide(1) in a setInterval with your desired delay (e.g. 4000ms). Consider clearing and restarting that interval on manual dot/arrow interaction, and pausing it on mouse hover or keyboard focus for better usability.

Yes — replace each .slide div's background gradient with a background-image, or replace the div's content with an <img> element sized to fill the slide with object-fit: cover.

Add a new .slide div inside the track and a matching dot button in the .dots container calling goToSlide with the next index. totalSlides is calculated automatically from the DOM, so no manual count needs updating.

The dots and arrows are native buttons with descriptive aria-labels ("Go to slide 2", "Previous slide"), and the dots container uses role="tablist" to signal a navigable group. All controls are reachable and operable via keyboard by default since they are real button elements.