22 CSS Image Carousels11 / 22
Onboarding Tutorial Slider
A phone-width, swipeable onboarding flow with illustration, copy and indicator dots — the first-run tutorial pattern for mobile apps. Built on scroll-snap with the modern CSS ::scroll-marker dots, so pagination is generated by the browser with a graceful fallback.
Published
The code
<section class="car-11" aria-label="App onboarding">
<div class="car-11__phone">
<ol class="car-11__scroller" tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys">
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#6a5cff,#9b8bff)"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" alt="Track your habits illustration"></div><h3 class="car-11__h">Track what matters</h3><p class="car-11__p">Log habits in two taps and watch streaks build over time.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#00b3a4,#4fd1c5)"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" alt="Get reminders illustration"></div><h3 class="car-11__h">Gentle reminders</h3><p class="car-11__p">Smart nudges at the right moment — never spammy, always optional.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#ff7a59,#ffb057)"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" alt="See progress illustration"></div><h3 class="car-11__h">See your progress</h3><p class="car-11__p">Weekly insights that show how far you've come, at a glance.</p></li>
</ol>
</div>
</section><section class="car-11" aria-label="App onboarding">
<div class="car-11__phone">
<ol class="car-11__scroller" tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys">
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#6a5cff,#9b8bff)"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" alt="Track your habits illustration"></div><h3 class="car-11__h">Track what matters</h3><p class="car-11__p">Log habits in two taps and watch streaks build over time.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#00b3a4,#4fd1c5)"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" alt="Get reminders illustration"></div><h3 class="car-11__h">Gentle reminders</h3><p class="car-11__p">Smart nudges at the right moment — never spammy, always optional.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#ff7a59,#ffb057)"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" alt="See progress illustration"></div><h3 class="car-11__h">See your progress</h3><p class="car-11__p">Weekly insights that show how far you've come, at a glance.</p></li>
</ol>
</div>
</section>.car-11,
.car-11 *,
.car-11 *::before,
.car-11 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-11 ::selection {
background: #6a5cff;
color: #fff;
}
.car-11 {
--accent: #6a5cff;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #eceafb;
display: flex;
align-items: center;
justify-content: center;
}
.car-11__phone {
width: 300px;
max-width: 100%;
background: #fff;
border-radius: 34px;
padding: 16px;
box-shadow: 0 30px 60px -34px rgba(40,30,120,.6);
border: 1px solid #e2ddf6;
}
.car-11__scroller {
list-style: none;
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
border-radius: 22px;
scrollbar-width: none;
scroll-marker-group: after;
}
.car-11__scroller::-webkit-scrollbar {
display: none;
}
.car-11__scroller:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
.car-11__step {
flex: 0 0 100%;
scroll-snap-align: center;
padding: 8px 20px 20px;
text-align: center;
}
.car-11__step::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: #c9c2f2;
margin: 4px;
cursor: pointer;
border: 0;
transition: transform .2s,background .2s;
}
.car-11__step::scroll-marker:target-current {
background: var(--accent);
transform: scale(1.35);
}
.car-11__scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.car-11__art {
aspect-ratio: 1/1;
border-radius: 20px;
overflow: hidden;
margin-bottom: 22px;
}
.car-11__art img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: luminosity;
opacity: .9;
}
.car-11__h {
font-size: 20px;
font-weight: 800;
color: #231d4a;
margin-bottom: 8px;
}
.car-11__p {
font-size: 14px;
line-height: 1.55;
color: #6b658c;
}
@media (prefers-reduced-motion: reduce) {
.car-11__scroller {
scroll-behavior: auto;
}
.car-11__step::scroll-marker {
transition: none;
}
}.car-11,
.car-11 *,
.car-11 *::before,
.car-11 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-11 ::selection {
background: #6a5cff;
color: #fff;
}
.car-11 {
--accent: #6a5cff;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #eceafb;
display: flex;
align-items: center;
justify-content: center;
}
.car-11__phone {
width: 300px;
max-width: 100%;
background: #fff;
border-radius: 34px;
padding: 16px;
box-shadow: 0 30px 60px -34px rgba(40,30,120,.6);
border: 1px solid #e2ddf6;
}
.car-11__scroller {
list-style: none;
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
border-radius: 22px;
scrollbar-width: none;
scroll-marker-group: after;
}
.car-11__scroller::-webkit-scrollbar {
display: none;
}
.car-11__scroller:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
.car-11__step {
flex: 0 0 100%;
scroll-snap-align: center;
padding: 8px 20px 20px;
text-align: center;
}
.car-11__step::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: #c9c2f2;
margin: 4px;
cursor: pointer;
border: 0;
transition: transform .2s,background .2s;
}
.car-11__step::scroll-marker:target-current {
background: var(--accent);
transform: scale(1.35);
}
.car-11__scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.car-11__art {
aspect-ratio: 1/1;
border-radius: 20px;
overflow: hidden;
margin-bottom: 22px;
}
.car-11__art img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: luminosity;
opacity: .9;
}
.car-11__h {
font-size: 20px;
font-weight: 800;
color: #231d4a;
margin-bottom: 8px;
}
.car-11__p {
font-size: 14px;
line-height: 1.55;
color: #6b658c;
}
@media (prefers-reduced-motion: reduce) {
.car-11__scroller {
scroll-behavior: auto;
}
.car-11__step::scroll-marker {
transition: none;
}
}/* No JavaScript — scroll-snap slider with browser-generated ::scroll-marker dots (Chromium 135+); slider works everywhere, dots are progressive enhancement. */
/* No JavaScript — scroll-snap slider with browser-generated ::scroll-marker dots (Chromium 135+); slider works everywhere, dots are progressive enhancement. */Here's a working CSS Image Carousel from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Onboarding Tutorial Slider
Source: https://codefronts.com/snippets/css-image-carousel/onboarding-tutorial-slider/
A phone-width, swipeable onboarding flow with illustration, copy and indicator dots — the first-run tutorial pattern for mobile apps. Built on scroll-snap with the modern CSS ::scroll-marker dots, so pagination is generated by the browser with a graceful fallback.
## HTML
```html
<section class="car-11" aria-label="App onboarding">
<div class="car-11__phone">
<ol class="car-11__scroller" tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys">
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#6a5cff,#9b8bff)"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" alt="Track your habits illustration"></div><h3 class="car-11__h">Track what matters</h3><p class="car-11__p">Log habits in two taps and watch streaks build over time.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#00b3a4,#4fd1c5)"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" alt="Get reminders illustration"></div><h3 class="car-11__h">Gentle reminders</h3><p class="car-11__p">Smart nudges at the right moment — never spammy, always optional.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#ff7a59,#ffb057)"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" alt="See progress illustration"></div><h3 class="car-11__h">See your progress</h3><p class="car-11__p">Weekly insights that show how far you've come, at a glance.</p></li>
</ol>
</div>
</section>
```
## CSS
```css
.car-11,
.car-11 *,
.car-11 *::before,
.car-11 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-11 ::selection {
background: #6a5cff;
color: #fff;
}
.car-11 {
--accent: #6a5cff;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #eceafb;
display: flex;
align-items: center;
justify-content: center;
}
.car-11__phone {
width: 300px;
max-width: 100%;
background: #fff;
border-radius: 34px;
padding: 16px;
box-shadow: 0 30px 60px -34px rgba(40,30,120,.6);
border: 1px solid #e2ddf6;
}
.car-11__scroller {
list-style: none;
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
border-radius: 22px;
scrollbar-width: none;
scroll-marker-group: after;
}
.car-11__scroller::-webkit-scrollbar {
display: none;
}
.car-11__scroller:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
.car-11__step {
flex: 0 0 100%;
scroll-snap-align: center;
padding: 8px 20px 20px;
text-align: center;
}
.car-11__step::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: #c9c2f2;
margin: 4px;
cursor: pointer;
border: 0;
transition: transform .2s,background .2s;
}
.car-11__step::scroll-marker:target-current {
background: var(--accent);
transform: scale(1.35);
}
.car-11__scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.car-11__art {
aspect-ratio: 1/1;
border-radius: 20px;
overflow: hidden;
margin-bottom: 22px;
}
.car-11__art img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: luminosity;
opacity: .9;
}
.car-11__h {
font-size: 20px;
font-weight: 800;
color: #231d4a;
margin-bottom: 8px;
}
.car-11__p {
font-size: 14px;
line-height: 1.55;
color: #6b658c;
}
@media (prefers-reduced-motion: reduce) {
.car-11__scroller {
scroll-behavior: auto;
}
.car-11__step::scroll-marker {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — scroll-snap slider with browser-generated ::scroll-marker dots (Chromium 135+); slider works everywhere, dots are progressive enhancement. */
```Here's a working CSS Image Carousel from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Onboarding Tutorial Slider
Source: https://codefronts.com/snippets/css-image-carousel/onboarding-tutorial-slider/
A phone-width, swipeable onboarding flow with illustration, copy and indicator dots — the first-run tutorial pattern for mobile apps. Built on scroll-snap with the modern CSS ::scroll-marker dots, so pagination is generated by the browser with a graceful fallback.
## HTML
```html
<section class="car-11" aria-label="App onboarding">
<div class="car-11__phone">
<ol class="car-11__scroller" tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys">
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#6a5cff,#9b8bff)"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" alt="Track your habits illustration"></div><h3 class="car-11__h">Track what matters</h3><p class="car-11__p">Log habits in two taps and watch streaks build over time.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#00b3a4,#4fd1c5)"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" alt="Get reminders illustration"></div><h3 class="car-11__h">Gentle reminders</h3><p class="car-11__p">Smart nudges at the right moment — never spammy, always optional.</p></li>
<li class="car-11__step"><div class="car-11__art" style="background:linear-gradient(135deg,#ff7a59,#ffb057)"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" alt="See progress illustration"></div><h3 class="car-11__h">See your progress</h3><p class="car-11__p">Weekly insights that show how far you've come, at a glance.</p></li>
</ol>
</div>
</section>
```
## CSS
```css
.car-11,
.car-11 *,
.car-11 *::before,
.car-11 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-11 ::selection {
background: #6a5cff;
color: #fff;
}
.car-11 {
--accent: #6a5cff;
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 20px;
background: #eceafb;
display: flex;
align-items: center;
justify-content: center;
}
.car-11__phone {
width: 300px;
max-width: 100%;
background: #fff;
border-radius: 34px;
padding: 16px;
box-shadow: 0 30px 60px -34px rgba(40,30,120,.6);
border: 1px solid #e2ddf6;
}
.car-11__scroller {
list-style: none;
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
border-radius: 22px;
scrollbar-width: none;
scroll-marker-group: after;
}
.car-11__scroller::-webkit-scrollbar {
display: none;
}
.car-11__scroller:focus-visible {
outline: 3px solid var(--accent);
outline-offset: 3px;
}
.car-11__step {
flex: 0 0 100%;
scroll-snap-align: center;
padding: 8px 20px 20px;
text-align: center;
}
.car-11__step::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: #c9c2f2;
margin: 4px;
cursor: pointer;
border: 0;
transition: transform .2s,background .2s;
}
.car-11__step::scroll-marker:target-current {
background: var(--accent);
transform: scale(1.35);
}
.car-11__scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.car-11__art {
aspect-ratio: 1/1;
border-radius: 20px;
overflow: hidden;
margin-bottom: 22px;
}
.car-11__art img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
mix-blend-mode: luminosity;
opacity: .9;
}
.car-11__h {
font-size: 20px;
font-weight: 800;
color: #231d4a;
margin-bottom: 8px;
}
.car-11__p {
font-size: 14px;
line-height: 1.55;
color: #6b658c;
}
@media (prefers-reduced-motion: reduce) {
.car-11__scroller {
scroll-behavior: auto;
}
.car-11__step::scroll-marker {
transition: none;
}
}
```
## JavaScript
```js
/* No JavaScript — scroll-snap slider with browser-generated ::scroll-marker dots (Chromium 135+); slider works everywhere, dots are progressive enhancement. */
```How this works
Steps are full-width snap panels in a scroll-snap-type:x mandatory scroller. The dot pagination uses the new CSS Carousel primitive: scroll-marker-group on the scroller plus a ::scroll-marker pseudo per item, so the browser renders one clickable dot per panel and tracks the active one via :target-current — no JS, no hand-written dots.
Where ::scroll-marker isn't supported yet, the scroller still snaps and swipes perfectly; the dots simply don't render, so nothing breaks. The scroller is focusable for keyboard paging.
Make it yours
- Recolour dots via
--accentin the::scroll-markerand:target-currentrules. - Add a step by adding one panel — the dot appears automatically.
- Change panel width/height to match your device frame.
- Swap the illustration block for a Lottie/video per step.
Gotchas — read before shipping
::scroll-marker/scroll-marker-groupship in Chromium 135+; treat the dots as progressive enhancement and keep the scroller usable without them.- Keep panels exactly viewport-width so snapping lands cleanly one step at a time.
- Provide a text 'Step N of M' or headings so progress isn't conveyed by dots alone.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 135+ | n/a | n/a | 135+ |
::scroll-marker + scroll-marker-group are new CSS Carousel features (Chromium 135+). The scroll-snap slider itself works everywhere; dots are progressive enhancement.