11 / 22
All 22 in 22 Tailwind CSS Image Carousel
- 01Tailwind E-commerce Product Gallery Carousel with Thumbnails
- 02Tailwind Shop the Look Multi-Item Card Slider with Scroll Snap
- 03Tailwind Infinite Logo Scroller Marquee with No JavaScript
- 04Tailwind Product Colour Variant Picker Slider
- 05Tailwind Full-Screen Hero Slideshow with Auto Cross-Fade
- 06Tailwind Testimonial Card Carousel with Star Ratings
- 07Tailwind Meet the Team Profile Slider with Circular Portraits
- 08Tailwind Portfolio Gallery Slider with Hover Caption Reveal
- 09Tailwind Netflix-Style Content Row Carousel with Hover Expand
- 10Tailwind Magazine Split-Screen Article Slider
- 11Tailwind Onboarding Slider with Native CSS Scroll-Marker Dots
- 12Tailwind Vertical Image Carousel with Y-Axis Scroll Snap
- 13Tailwind 3D Coverflow Carousel with Perspective Transforms
- 14Tailwind Cross-Fade Image Slideshow with Manual Controls
- 15Tailwind Dual Opposite-Direction Marquee Slider
- 16Tailwind Dot Pagination Carousel with Target and Has Selectors
- 17Tailwind Prev / Next Arrow Carousel with Native Scroll Buttons
- 18Tailwind Before / After Image Comparison Slider
- 19Tailwind Accordion Expanding Image Panel Carousel
- 20Tailwind Autoplay Slider with Keyframe Track and Progress Bars
- 21Tailwind Zoom-on-Hover Image Gallery Slider
- 22Tailwind Parallax Text-Over-Image Story Slider
Tailwind Onboarding Slider with Native CSS Scroll-Marker Dots
A phone-width first-run tutorial with illustration, copy and indicator dots. The dots are not markup at all — ::scroll-marker generates one per panel and tracks the active one, which is the first carousel primitive the browser has ever owned.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tcar-11 w-full min-h-svh grid place-items-center bg-tcar11-bg font-tcar11-sans px-5 py-10" aria-label="App onboarding">
<div class="w-[300px] max-w-full bg-white rounded-[34px] p-4 border border-tcar11-edge shadow-[0_30px_60px_-34px_oklch(0.3_0.08_290/0.6)]">
<ol tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys" class="tcar11-scroller list-none flex overflow-x-auto m-0 p-0 rounded-[22px] snap-x snap-mandatory [scrollbar-width:none] [scroll-marker-group:after] focus-visible:outline-3 focus-visible:outline-tcar11-acc focus-visible:outline-offset-[3px]">
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#6a5cff,#9b8bff)]"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" decoding="async" alt="Track your habits illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 1 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Track what matters</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Log habits in two taps and watch streaks build over time.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#00b3a4,#4fd1c5)]"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" loading="lazy" decoding="async" alt="Get reminders illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 2 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Gentle reminders</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Smart nudges at the right moment — never spammy, always optional.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#ff7a59,#ffb057)]"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" loading="lazy" decoding="async" alt="See progress illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 3 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">See your progress</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Weekly insights that show how far you've come, at a glance.</p>
</li>
</ol>
</div>
</section><section class="tcar-11 w-full min-h-svh grid place-items-center bg-tcar11-bg font-tcar11-sans px-5 py-10" aria-label="App onboarding">
<div class="w-[300px] max-w-full bg-white rounded-[34px] p-4 border border-tcar11-edge shadow-[0_30px_60px_-34px_oklch(0.3_0.08_290/0.6)]">
<ol tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys" class="tcar11-scroller list-none flex overflow-x-auto m-0 p-0 rounded-[22px] snap-x snap-mandatory [scrollbar-width:none] [scroll-marker-group:after] focus-visible:outline-3 focus-visible:outline-tcar11-acc focus-visible:outline-offset-[3px]">
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#6a5cff,#9b8bff)]"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" decoding="async" alt="Track your habits illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 1 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Track what matters</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Log habits in two taps and watch streaks build over time.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#00b3a4,#4fd1c5)]"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" loading="lazy" decoding="async" alt="Get reminders illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 2 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Gentle reminders</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Smart nudges at the right moment — never spammy, always optional.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#ff7a59,#ffb057)]"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" loading="lazy" decoding="async" alt="See progress illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 3 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">See your progress</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Weekly insights that show how far you've come, at a glance.</p>
</li>
</ol>
</div>
</section>@import "tailwindcss";
@theme {
--color-tcar11-bg: #eceafb;
--color-tcar11-bg: oklch(0.93 0.025 285);
--color-tcar11-edge: #e2ddf6;
--color-tcar11-edge: oklch(0.91 0.025 285);
--color-tcar11-ink: #231d4a;
--color-tcar11-ink: oklch(0.28 0.08 285);
--color-tcar11-mute: #5f5982;
--color-tcar11-mute: oklch(0.48 0.05 285);
--color-tcar11-dot: #c9c2f2;
--color-tcar11-dot: oklch(0.82 0.06 285);
--color-tcar11-acc: #6a5cff;
--color-tcar11-acc: oklch(0.55 0.22 285);
--font-tcar11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tcar-11 {
width: 100%;
min-height: 100svh;
}
.tcar-11 ::selection {
background: var(--color-tcar11-acc);
color: #fff;
}
.tcar11-scroller::-webkit-scrollbar {
display: none;
}
/* CSS Carousel primitives: no utility form exists, and inventing one would obscure them. */
.tcar11-scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.tcar11-scroller li::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--color-tcar11-dot);
margin: 6px;
cursor: pointer;
border: 0;
transition: scale .2s, background-color .2s;
}
.tcar11-scroller li::scroll-marker:target-current {
background: var(--color-tcar11-acc);
scale: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.tcar11-scroller {
scroll-behavior: auto;
}
.tcar11-scroller li::scroll-marker {
transition: none;
}
}@import "tailwindcss";
@theme {
--color-tcar11-bg: #eceafb;
--color-tcar11-bg: oklch(0.93 0.025 285);
--color-tcar11-edge: #e2ddf6;
--color-tcar11-edge: oklch(0.91 0.025 285);
--color-tcar11-ink: #231d4a;
--color-tcar11-ink: oklch(0.28 0.08 285);
--color-tcar11-mute: #5f5982;
--color-tcar11-mute: oklch(0.48 0.05 285);
--color-tcar11-dot: #c9c2f2;
--color-tcar11-dot: oklch(0.82 0.06 285);
--color-tcar11-acc: #6a5cff;
--color-tcar11-acc: oklch(0.55 0.22 285);
--font-tcar11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tcar-11 {
width: 100%;
min-height: 100svh;
}
.tcar-11 ::selection {
background: var(--color-tcar11-acc);
color: #fff;
}
.tcar11-scroller::-webkit-scrollbar {
display: none;
}
/* CSS Carousel primitives: no utility form exists, and inventing one would obscure them. */
.tcar11-scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.tcar11-scroller li::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--color-tcar11-dot);
margin: 6px;
cursor: pointer;
border: 0;
transition: scale .2s, background-color .2s;
}
.tcar11-scroller li::scroll-marker:target-current {
background: var(--color-tcar11-acc);
scale: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.tcar11-scroller {
scroll-behavior: auto;
}
.tcar11-scroller li::scroll-marker {
transition: none;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tcar11-bg: #eceafb;
--color-tcar11-bg: oklch(0.93 0.025 285);
--color-tcar11-edge: #e2ddf6;
--color-tcar11-edge: oklch(0.91 0.025 285);
--color-tcar11-ink: #231d4a;
--color-tcar11-ink: oklch(0.28 0.08 285);
--color-tcar11-mute: #5f5982;
--color-tcar11-mute: oklch(0.48 0.05 285);
--color-tcar11-dot: #c9c2f2;
--color-tcar11-dot: oklch(0.82 0.06 285);
--color-tcar11-acc: #6a5cff;
--color-tcar11-acc: oklch(0.55 0.22 285);
--font-tcar11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tcar-11 {
width: 100%;
min-height: 100svh;
}
.tcar-11 ::selection {
background: var(--color-tcar11-acc);
color: #fff;
}
.tcar11-scroller::-webkit-scrollbar {
display: none;
}
/* CSS Carousel primitives: no utility form exists, and inventing one would obscure them. */
.tcar11-scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.tcar11-scroller li::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--color-tcar11-dot);
margin: 6px;
cursor: pointer;
border: 0;
transition: scale .2s, background-color .2s;
}
.tcar11-scroller li::scroll-marker:target-current {
background: var(--color-tcar11-acc);
scale: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.tcar11-scroller {
scroll-behavior: auto;
}
.tcar11-scroller li::scroll-marker {
transition: none;
}
}@import "tailwindcss";
@theme {
--color-tcar11-bg: #eceafb;
--color-tcar11-bg: oklch(0.93 0.025 285);
--color-tcar11-edge: #e2ddf6;
--color-tcar11-edge: oklch(0.91 0.025 285);
--color-tcar11-ink: #231d4a;
--color-tcar11-ink: oklch(0.28 0.08 285);
--color-tcar11-mute: #5f5982;
--color-tcar11-mute: oklch(0.48 0.05 285);
--color-tcar11-dot: #c9c2f2;
--color-tcar11-dot: oklch(0.82 0.06 285);
--color-tcar11-acc: #6a5cff;
--color-tcar11-acc: oklch(0.55 0.22 285);
--font-tcar11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tcar-11 {
width: 100%;
min-height: 100svh;
}
.tcar-11 ::selection {
background: var(--color-tcar11-acc);
color: #fff;
}
.tcar11-scroller::-webkit-scrollbar {
display: none;
}
/* CSS Carousel primitives: no utility form exists, and inventing one would obscure them. */
.tcar11-scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.tcar11-scroller li::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--color-tcar11-dot);
margin: 6px;
cursor: pointer;
border: 0;
transition: scale .2s, background-color .2s;
}
.tcar11-scroller li::scroll-marker:target-current {
background: var(--color-tcar11-acc);
scale: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.tcar11-scroller {
scroll-behavior: auto;
}
.tcar11-scroller li::scroll-marker {
transition: none;
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The
::scroll-markerrules readvar(--color-tcar11-dot)andvar(--color-tcar11-acc)— the same@themetokens the utilities use. Tokens are the bridge: CSS that cannot become a utility still shares one source of truth with the markup. [scroll-marker-group:after]is the only Tailwind-side line the feature needs. Everything else is genuinely new CSS with no utility surface, and pretending otherwise with arbitrary variants would make it harder to read, not easier.- UI change from the CSS original: each panel gained a literal "Step N of 3" line. The original's own gotcha note said progress should not be dot-only, then shipped it dot-only; on every non-Chromium browser those dots do not exist at all.
Here's a working CSS demo 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: Tailwind Onboarding Slider with Native CSS Scroll-Marker Dots
Source: https://codefronts.com/snippets/tailwind-css-image-carousel/tailwind-onboarding-slider-scroll-marker-dots/
A phone-width first-run tutorial with illustration, copy and indicator dots. The dots are not markup at all — ::scroll-marker generates one per panel and tracks the active one, which is the first carousel primitive the browser has ever owned.
## HTML
```html
<section class="tcar-11 w-full min-h-svh grid place-items-center bg-tcar11-bg font-tcar11-sans px-5 py-10" aria-label="App onboarding">
<div class="w-[300px] max-w-full bg-white rounded-[34px] p-4 border border-tcar11-edge shadow-[0_30px_60px_-34px_oklch(0.3_0.08_290/0.6)]">
<ol tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys" class="tcar11-scroller list-none flex overflow-x-auto m-0 p-0 rounded-[22px] snap-x snap-mandatory [scrollbar-width:none] [scroll-marker-group:after] focus-visible:outline-3 focus-visible:outline-tcar11-acc focus-visible:outline-offset-[3px]">
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#6a5cff,#9b8bff)]"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" decoding="async" alt="Track your habits illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 1 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Track what matters</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Log habits in two taps and watch streaks build over time.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#00b3a4,#4fd1c5)]"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" loading="lazy" decoding="async" alt="Get reminders illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 2 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Gentle reminders</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Smart nudges at the right moment — never spammy, always optional.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#ff7a59,#ffb057)]"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" loading="lazy" decoding="async" alt="See progress illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 3 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">See your progress</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Weekly insights that show how far you've come, at a glance.</p>
</li>
</ol>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcar11-bg: #eceafb;
--color-tcar11-bg: oklch(0.93 0.025 285);
--color-tcar11-edge: #e2ddf6;
--color-tcar11-edge: oklch(0.91 0.025 285);
--color-tcar11-ink: #231d4a;
--color-tcar11-ink: oklch(0.28 0.08 285);
--color-tcar11-mute: #5f5982;
--color-tcar11-mute: oklch(0.48 0.05 285);
--color-tcar11-dot: #c9c2f2;
--color-tcar11-dot: oklch(0.82 0.06 285);
--color-tcar11-acc: #6a5cff;
--color-tcar11-acc: oklch(0.55 0.22 285);
--font-tcar11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tcar-11 {
width: 100%;
min-height: 100svh;
}
.tcar-11 ::selection {
background: var(--color-tcar11-acc);
color: #fff;
}
.tcar11-scroller::-webkit-scrollbar {
display: none;
}
/* CSS Carousel primitives: no utility form exists, and inventing one would obscure them. */
.tcar11-scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.tcar11-scroller li::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--color-tcar11-dot);
margin: 6px;
cursor: pointer;
border: 0;
transition: scale .2s, background-color .2s;
}
.tcar11-scroller li::scroll-marker:target-current {
background: var(--color-tcar11-acc);
scale: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.tcar11-scroller {
scroll-behavior: auto;
}
.tcar11-scroller li::scroll-marker {
transition: none;
}
}
```Here's a working CSS demo 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: Tailwind Onboarding Slider with Native CSS Scroll-Marker Dots
Source: https://codefronts.com/snippets/tailwind-css-image-carousel/tailwind-onboarding-slider-scroll-marker-dots/
A phone-width first-run tutorial with illustration, copy and indicator dots. The dots are not markup at all — ::scroll-marker generates one per panel and tracks the active one, which is the first carousel primitive the browser has ever owned.
## HTML
```html
<section class="tcar-11 w-full min-h-svh grid place-items-center bg-tcar11-bg font-tcar11-sans px-5 py-10" aria-label="App onboarding">
<div class="w-[300px] max-w-full bg-white rounded-[34px] p-4 border border-tcar11-edge shadow-[0_30px_60px_-34px_oklch(0.3_0.08_290/0.6)]">
<ol tabindex="0" role="group" aria-label="Onboarding steps, swipe or use arrow keys" class="tcar11-scroller list-none flex overflow-x-auto m-0 p-0 rounded-[22px] snap-x snap-mandatory [scrollbar-width:none] [scroll-marker-group:after] focus-visible:outline-3 focus-visible:outline-tcar11-acc focus-visible:outline-offset-[3px]">
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#6a5cff,#9b8bff)]"><img src="https://picsum.photos/seed/onb-1/400/400" width="400" height="400" decoding="async" alt="Track your habits illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 1 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Track what matters</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Log habits in two taps and watch streaks build over time.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#00b3a4,#4fd1c5)]"><img src="https://picsum.photos/seed/onb-2/400/400" width="400" height="400" loading="lazy" decoding="async" alt="Get reminders illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 2 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">Gentle reminders</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Smart nudges at the right moment — never spammy, always optional.</p>
</li>
<li class="flex-none basis-full snap-center px-5 pt-2 pb-5 text-center">
<div class="aspect-square rounded-[20px] overflow-hidden mb-[22px] bg-[linear-gradient(135deg,#ff7a59,#ffb057)]"><img src="https://picsum.photos/seed/onb-3/400/400" width="400" height="400" loading="lazy" decoding="async" alt="See progress illustration" class="size-full object-cover block mix-blend-luminosity opacity-90"></div>
<p class="text-[11px] font-semibold tracking-[0.14em] uppercase text-tcar11-mute mb-2">Step 3 of 3</p>
<h3 class="text-xl font-extrabold text-tcar11-ink mb-2">See your progress</h3>
<p class="text-sm leading-[1.55] text-tcar11-mute m-0">Weekly insights that show how far you've come, at a glance.</p>
</li>
</ol>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcar11-bg: #eceafb;
--color-tcar11-bg: oklch(0.93 0.025 285);
--color-tcar11-edge: #e2ddf6;
--color-tcar11-edge: oklch(0.91 0.025 285);
--color-tcar11-ink: #231d4a;
--color-tcar11-ink: oklch(0.28 0.08 285);
--color-tcar11-mute: #5f5982;
--color-tcar11-mute: oklch(0.48 0.05 285);
--color-tcar11-dot: #c9c2f2;
--color-tcar11-dot: oklch(0.82 0.06 285);
--color-tcar11-acc: #6a5cff;
--color-tcar11-acc: oklch(0.55 0.22 285);
--font-tcar11-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
}
.tcar-11 {
width: 100%;
min-height: 100svh;
}
.tcar-11 ::selection {
background: var(--color-tcar11-acc);
color: #fff;
}
.tcar11-scroller::-webkit-scrollbar {
display: none;
}
/* CSS Carousel primitives: no utility form exists, and inventing one would obscure them. */
.tcar11-scroller::scroll-marker-group {
display: flex;
justify-content: center;
align-items: center;
padding-top: 14px;
}
.tcar11-scroller li::scroll-marker {
content: "";
width: 9px;
height: 9px;
border-radius: 50%;
background: var(--color-tcar11-dot);
margin: 6px;
cursor: pointer;
border: 0;
transition: scale .2s, background-color .2s;
}
.tcar11-scroller li::scroll-marker:target-current {
background: var(--color-tcar11-acc);
scale: 1.35;
}
@media (prefers-reduced-motion: reduce) {
.tcar11-scroller {
scroll-behavior: auto;
}
.tcar11-scroller li::scroll-marker {
transition: none;
}
}
```How this works
The browser owns the pagination. scroll-marker-group:after plus a ::scroll-marker per item gives one clickable dot per panel, with the active one matched by :target-current. No dot markup, no JS, no active-state bookkeeping.
Everything about it stays in CSS. This is the demo where the port draws its line: the layout, the phone frame and the panels are utilities; the four rules describing a pseudo-element Tailwind has never heard of live in the stylesheet, namespaced under .tcar11-scroller.
It degrades to nothing, which is correct. Where ::scroll-marker is unsupported the dots simply do not render and the panel scroller still swipes, snaps and keyboard-pages. The "Step N of 3" line means no user is left without a progress signal.
Make it yours
- Dot colour: the two
::scroll-markerrules, which read the same--color-tcar11-acctoken as the utilities. - Add a step: add one panel — the dot appears by itself.
- Frame size:
w-[300px]and the panel padding. - Swap each illustration block for a video or Lottie without touching the scroller.
Gotchas — read before shipping
::scroll-markerandscroll-marker-groupare Chromium 135+. Treat the dots as enhancement; never make them the only way to tell which step you are on.- Panels must be exactly
basis-full, or snapping lands between steps. - Do not try to write
scroll-marker:bg-tcar11-acc— Tailwind has no such variant, and inventing one with an arbitrary variant would be less readable than the four CSS rules.
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 in every browser at Tailwind v4's usual floor; the dots are progressive enhancement.