Scroll-snap carousels, auto-play image sliders with dot indicators and arrow navigation, multi-item responsive carousels, and card-based sliders \u2014 all pure CSS and vanilla JavaScript.
Basic Scroll Snap
The simplest carousel uses CSS scroll-snap-type and scroll-snap-align for native-feeling horizontal scrolling with snapping. No JavaScript required. The track uses flexbox with horizontal overflow, and each slide snaps to the start of the container on scroll.
Scroll snap is a CSS-only solution for carousel behavior. The key properties are scroll-snap-type on the container (mandatory or proximity) and scroll-snap-align on each child (start, center, or end).
scroll-snap.css
CSS
1
/* Container: mandatory snap on X axis */
2
.carousel-track {
3
display: flex;
4
overflow-x: auto;
5
scroll-snap-type: x mandatory;
6
scroll-behavior: smooth;
7
gap: 12px;
8
}
9
10
/* Each slide: snap to start */
11
.carousel-slide {
12
flex: 0 0 100%;
13
scroll-snap-align: start;
14
}
15
16
/* Optional: hide scrollbar */
17
.carousel-track::-webkit-scrollbar {
18
display: none;
19
}
20
.carousel-track {
21
scrollbar-width: none;
22
}
Arrow Navigation
A carousel with previous/next arrow buttons that scroll to the adjacent slide. The arrows disable at boundaries and the active slide is tracked for the dot indicators. Arrow buttons overlay the carousel with absolute positioning.
An auto-advancing carousel that pauses on hover and resumes when the cursor leaves. A progress bar shows the time remaining before the next transition. Uses setTimeout for timing and resets the CSS animation on each slide change.
autoplay
Live
untitled.html
HTML
1
<div class="auto-carousel" id="auto-carousel">
2
<div class="auto-track" id="auto-track">
3
<div class="auto-slide">
4
<div class="auto-card gradient-1">
5
<div class="auto-body">
6
<span class="auto-badge">
7
Featured
8
</span>
9
<h3>
10
Design Systems
11
</h3>
12
<p>
13
Building scalable component libraries with consistent tokens, patterns, and documentation.
14
</p>
15
<button class="auto-btn">
16
Learn More
17
</button>
18
</div>
19
</div>
20
</div>
21
<div class="auto-slide">
22
<div class="auto-card gradient-2">
23
<div class="auto-body">
24
<span class="auto-badge">
25
New
26
</span>
27
<h3>
28
Edge Computing
29
</h3>
30
<p>
31
Deploy serverless functions to 200+ edge locations worldwide for sub-millisecond latency.
32
</p>
33
<button class="auto-btn">
34
Learn More
35
</button>
36
</div>
37
</div>
38
</div>
39
<div class="auto-slide">
40
<div class="auto-card gradient-3">
41
<div class="auto-body">
42
<span class="auto-badge">
43
Popular
44
</span>
45
<h3>
46
AI Workflows
47
</h3>
48
<p>
49
Orchestrate multi-step AI pipelines with automatic retries and cost optimization.
Shows multiple items at once with scroll snap. Each card snaps into place using flex: 0 0 calc(33.333% - 7px) for three visible items, and the last items fill the remaining space. Ideal for product grids and card lists.
A content-focused carousel for testimonials and quotes with author details, star ratings, and smooth crossfade transitions between items. Auto-advances every 5 seconds with manual dot navigation.
testimonial
Live
untitled.html
HTML
1
<div class="testi-carousel" id="testi-carousel">
2
<div class="testi-slide active" id="ts-0">
3
<div class="testi-stars">
4
★★★★★
5
</div>
6
<blockquote class="testi-quote">
7
"This design system reduced our component development time by 60%. The token architecture is incredibly well thought out."
8
</blockquote>
9
<div class="testi-author">
10
<div class="testi-avatar">
11
SK
12
</div>
13
<div>
14
<span class="testi-name">
15
Sarah Kim
16
</span>
17
<span class="testi-role">
18
Engineering Lead, Vercel
19
</span>
20
</div>
21
</div>
22
</div>
23
<div class="testi-slide" id="ts-1">
24
<div class="testi-stars">
25
★★★★★
26
</div>
27
<blockquote class="testi-quote">
28
"We migrated our entire frontend in 3 weeks. The component API is consistent and the documentation is outstanding."
29
</blockquote>
30
<div class="testi-author">
31
<div class="testi-avatar">
32
MR
33
</div>
34
<div>
35
<span class="testi-name">
36
Marcus Rivera
37
</span>
38
<span class="testi-role">
39
CTO, Stripe
40
</span>
41
</div>
42
</div>
43
</div>
44
<div class="testi-slide" id="ts-2">
45
<div class="testi-stars">
46
★★★★★
47
</div>
48
<blockquote class="testi-quote">
49
"The accessibility-first approach means we don't have to retrofit a11y later. Every component works with screen readers out of the box."
50
</blockquote>
51
<div class="testi-author">
52
<div class="testi-avatar">
53
AL
54
</div>
55
<div>
56
<span class="testi-name">
57
Aisha Liang
58
</span>
59
<span class="testi-role">
60
Designer, Figma
61
</span>
62
</div>
63
</div>
64
</div>
65
<div class="testi-nav">
66
<button class="tn active" data-i="0">
67
</button>
68
<button class="tn" data-i="1">
69
</button>
70
<button class="tn" data-i="2">
71
</button>
72
</div>
73
</div>
untitled.css
CSS
1
.testi-carousel {
2
max-width:380px;
3
margin:16px auto;
4
padding:24px;
5
background:#111;
6
border:1px solid #222;
7
border-radius:12px;
8
position:relative;
9
font-family:system-ui,
10
sans-serif;
11
min-height:220px
12
}
13
.testi-slide {
14
display:none;
15
flex-direction:column;
16
gap:16px;
17
animation:fadeIn .3s ease
18
}
19
.testi-slide.active {
20
display:flex
21
}
22
.testi-stars {
23
color:#FBBF24;
24
font-size:14px;
25
letter-spacing:2px
26
}
27
.testi-quote {
28
font-size:13px;
29
color:#C0C0C0;
30
line-height:1.6;
31
margin:0;
32
font-style:italic;
33
border-left:2px solid #00FF41;
34
padding-left:14px
35
}
36
.testi-author {
37
display:flex;
38
align-items:center;
39
gap:10px
40
}
41
.testi-avatar {
42
width:36px;
43
height:36px;
44
border-radius:50%;
45
background:#1A1A2E;
46
display:flex;
47
align-items:center;
48
justify-content:center;
49
font-size:11px;
50
font-weight:700;
51
color:#00FF41
52
}
53
.testi-name {
54
display:block;
55
font-size:12px;
56
font-weight:600;
57
color:#E0E0E0
58
}
59
.testi-role {
60
display:block;
61
font-size:10px;
62
color:#666;
63
margin-top:2px
64
}
65
.testi-nav {
66
display:flex;
67
gap:6px;
68
justify-content:center;
69
position:absolute;
70
bottom:16px;
71
left:50%;
72
transform:translateX(-50%)
73
}
74
.tn {
75
width:6px;
76
height:6px;
77
border-radius:50%;
78
border:none;
79
background:#333;
80
cursor:pointer;
81
transition:all .2s;
82
padding:0
83
}
84
.tn.active {
85
background:#00FF41;
86
width:18px;
87
border-radius:3px
88
}
89
@keyframes fadeIn {
90
from {
91
opacity:0;
92
transform:translateY(8px)
93
}
94
to {
95
opacity:1;
96
transform:translateY(0)
97
}
98
}
untitled.js
JavaScript
1
let cur=0;const slides=document.querySelectorAll('.testi-slide');const tns=document.querySelectorAll('.tn');function show(i){slides.forEach(s=>s.classList.remove('active'));tns.forEach(t=>t.classList.remove('active'));slides[i].classList.add('active');tns[i].classList.add('active');cur=i}tns.forEach(t=>t.addEventListener('click',()=>show(parseInt(t.dataset.i))));setInterval(()=>show((cur+1)%slides.length),5000)
preview
Thumbnail Navigation
A main image with thumbnail strip below for direct slide selection. Clicking a thumbnail jumps to that slide and highlights the active thumbnail. The strip syncs bidirectionally with the main view via scroll position.
The scroll-snap carousel uses a track container with scroll-snap-type: x mandatory and child slides with scroll-snap-align: start. Arrow buttons use scrollTo() for smooth transitions. Always include aria-label on navigation buttons.
carousel-structure.html
HTML
1
<div class="carousel-track" style="scroll-snap-type: x mandatory">
Use CSS scroll-snap-type for simple carousels \u2014 it provides native-feeling snap behavior with zero JavaScript. Only reach for JS-driven transitions when you need crossfade effects or complex timing control.
⚠
warning
Auto-playing carousels should always pause on hover and respect the prefers-reduced-motion media query. Some users experience motion sickness or vestibular disorders from moving content.
✓
best practice
Include aria-labelon arrow buttons ("Previous slide" / "Next slide") and role="tablist" on dot indicator containers for screen reader accessibility.
Hide scrollbar with scrollbar-width: none and ::-webkit-scrollbar for a cleaner appearance across all browsers.
Use overflow: hidden on the carousel container to clip slides at the boundary.
For multi-item carousels, calculate item width as calc(100% / visibleCount - gap) to fit exactly N items per viewport.
Add scroll-behavior: smooth for animated scrolling between slides without JavaScript.
On mobile, support swipe via native touch scrolling \u2014 avoid intercepting touch events unless you need custom gestures.
For auto-play, use a 4-5 second interval \u2014 fast enough to maintain interest but slow enough to read the content.