Auto-Play Carousel — Free HTML CSS JS Snippet
Auto-Play Carousel · Layouts · Plain HTML, CSS & JS · Live preview
What's included
Features
translateX(-n*100%) on the track — no DOM manipulation, just a single CSS property change per transition.linear CSS transition timed to match the 4-second interval, giving users a clear visual countdown.width and border-radius transitions communicate slide position more clearly than a simple colour change alone.mouseenter clears the advance interval and progress animation. mouseleave restarts both from zero — the user always gets a full read time after re-entering.pointerdown/pointerup handler on the track captures both mouse drags and touch swipes. Deltas over 40px trigger navigation.aria-live="polite" on the container and per-slide aria-hidden updates ensure screen readers announce new content without interrupting user actions.cubic-bezier(0.77,0,0.18,1) — an aggressive ease-in with smooth landing that gives the slide a confident, high-quality feel.About this UI Snippet
Auto-Play Carousel — CSS Transform Track, Progress Bar Timer & Pointer Swipe

A carousel is one of the most requested UI patterns in frontend development — showcasing featured content, hero slides, testimonials, or product images in a compact rotating format. This snippet delivers a production-ready auto-playing carousel in pure HTML, CSS, and vanilla JavaScript with zero dependencies: a sliding track with smooth cubic-bezier transitions, animated dot indicators, arrow navigation, a visual progress bar, pause-on-hover, and pointer-based swipe support for touch screens.
The carousel is used across virtually every category of web project — SaaS landing pages, e-commerce hero sections, portfolio showcases, marketing sites, and dashboards. Getting the implementation right matters: jank-free transitions, accessible ARIA attributes, correct swipe detection, and pause-on-hover are all expected by users and required for production quality.
The sliding track mechanism
The core of the carousel is a display: flex track containing all slides as min-width: 100% children. Navigating to slide n sets transform: translateX(-n * 100%) on the track, which shifts the entire row by one viewport-width unit. The CSS transition: transform 0.55s cubic-bezier(0.77,0,0.18,1) applies a custom easing — fast start, smooth deceleration — that feels cinematic rather than mechanical.
The progress bar timer
Each auto-play cycle is visualised with a bottom progress bar that grows from 0% to 100% width over the same duration as the interval (4 seconds). The animation uses a width CSS transition set to linear with the exact interval duration, so the bar and the slide advance in sync. When the user navigates manually, the transition is reset to none (forcing a synchronous paint), then re-applied on the next requestAnimationFrame — this reliably restarts the animation from 0% without a flash.
Dot indicators with pill active state
Each dot starts as a 7px circle. The active dot expands to a 22px-wide pill via width and border-radius transitions — a pattern that communicates both current position and progress direction more clearly than a simple colour change. Dots are <button> elements with role="tab" for keyboard and screen reader accessibility.
Pause on hover
mouseenter clears both the slide interval and progress timers. mouseleave restarts them. This prevents the carousel from advancing while the user is reading content — a standard UX expectation that many basic carousel implementations miss.
Swipe support via Pointer Events API
Rather than separate touch and mouse event handlers, the snippet uses a single pointerdown/pointerup pair on the track. The delta (clientX end minus start) determines direction: left-swipe advances, right-swipe retreats. Any delta larger than 40px counts as an intentional swipe. The Pointer Events API handles both mouse drags and touch swipes with one code path.
Accessibility
Each slide has aria-hidden="false" (current) or aria-hidden="true" (hidden), updated on every navigation. The container has role="region" and aria-live="polite", and arrow buttons have aria-label. Dot buttons have role="tab" and individual aria-label values. Pair with an animated tabs component for tabbed content that doesn't auto-advance.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Instead of tracing every timer reset by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the progress bar's transition is set to none and reset to 0% before being re-applied on the next requestAnimationFrame, or why goTo() guards against overlapping calls with the isAnimating flag. The same assistant can help optimize it — ask whether the single pointerdown/pointerup pair for swipe detection could misfire on a simple tap-and-hold versus an intentional drag, and how you'd distinguish them more robustly. It's also a great partner for extending the carousel: ask it to add keyboard arrow-key navigation, support variable slide durations per slide, or convert it into a multi-slide-visible layout while keeping the progress bar and dot indicators in sync. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build an auto-playing content carousel in plain HTML, CSS, and JavaScript — no library, no Swiper, no jQuery.
Requirements:
- A flex track containing full-width slides, navigated by setting a single CSS transform (translateX by negative current-index times 100 percent) on the track element, with a cubic-bezier transition for the slide motion — not by hiding/showing individual slide elements.
- An auto-advance timer plus a synchronized progress bar: the bar must animate its width from 0 to 100 percent using a linear CSS transition timed to match the exact auto-advance interval, and restarting the cycle (whether from a manual navigation or an auto-advance) must reset the transition to none, snap the width back to 0 percent, then re-apply the timed transition inside a requestAnimationFrame so the animation reliably restarts from zero without a visual flash.
- Prevent overlapping transitions: track an "is animating" flag that blocks new navigation calls until the current slide transition's transitionend event fires.
- Dot indicators where the active dot is visually distinct (not just a color change) from the inactive ones, each dot clickable to jump directly to its slide.
- Pause the auto-advance timer and progress bar entirely on mouseenter over the carousel, and resume both cleanly (restarting the progress bar from zero) on mouseleave.
- Support swipe/drag navigation using the Pointer Events API (not separate touch and mouse handlers) by capturing the horizontal delta between pointerdown and pointerup, and only triggering navigation if that delta exceeds a minimum threshold in pixels.
- Every slide must carry correct aria-hidden state reflecting whether it's the current slide, updated on every navigation.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSDrop the three blocks into your page. A full-width carousel appears with four coloured slides, arrow buttons, dot indicators, and a bottom progress bar.
- 2Watch the auto-playThe carousel advances every 4 seconds. The progress bar at the bottom fills left-to-right, giving users a visual count of when the next slide arrives.
- 3Click the arrows or dotsArrow buttons navigate prev/next. Dot buttons jump to any slide directly. Both reset the timer so the current slide gets its full 4-second display time.
- 4Swipe on touch devicesDrag the track left or right — swipes larger than 40px trigger navigation. Works with both touch and mouse pointer events.
- 5Hover to pauseMove the mouse over the carousel and it pauses. The progress bar freezes. Move away and it resumes — the full 4 seconds starts again from 0.
- 6Customise slide contentEach
.slidediv is independent — change the gradient background, tag text, heading, description, button label, and SVG illustration. Add or remove slides and the dot system updates automatically.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Change the INTERVAL constant at the top of the JS (default 4000ms). The progress bar animation automatically adjusts to match — it reads the same constant.
Duplicate any .slide div in the HTML and update its content. The JS counts slides with track.querySelectorAll(".slide") and the dot builder loops over that count — no JS changes needed.
Remove the startProgress() call at the bottom of the JS and remove the mouseenter/mouseleave listeners. The arrows and dots will still work. You can also set INTERVAL to a very large number like 99999.
Click the "↓ React" export button above the preview. The state becomes const [current, setCurrent] = useState(0). The setInterval goes in a useEffect with a cleanup return. The progress bar animation uses a key={current} on the bar element to force remount on each slide change, which restarts the CSS animation automatically.
Yes — change each slide's min-width from 100% to 33.333% and update the translateX calculation to current * (100/3)%. You will also need to guard the advance logic against going past the last group.