CSS Scroll Carousel Generator

Build a zero-JavaScript carousel with dot navigation using ::scroll-marker and scroll-snap

::scroll-marker, ::scroll-marker-group, and ::scroll-button() have full support in Chrome/Edge 135+ and Safari 19+ as of 2026, but remain behind a flag in Firefox. The carousel still scrolls and snaps everywhere via plain scroll-snap-type — only the generated dots/buttons are progressive enhancement.

Live preview

Slide 1
Slide 2
Slide 3
Slide 4
Slide 5

Preview shows the scroll-snap behavior only — dots/buttons need the generated CSS below rendered by a browser that supports them.

<ul class="carousel">
    <li class="carousel-item">Slide 1</li>
    <li class="carousel-item">Slide 2</li>
    <li class="carousel-item">Slide 3</li>
    <li class="carousel-item">Slide 4</li>
    <li class="carousel-item">Slide 5</li>
</ul>
.carousel {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  gap: 16px;
  list-style: none;
  padding: 0;
  margin: 0;
  scroll-marker-group: after;
}

.carousel-item {
  scroll-snap-align: start;
  flex: 0 0 auto;
  width: 240px;
}

/* Auto-generated dot navigation — one ::scroll-marker per item, grouped by ::scroll-marker-group */
.carousel::scroll-marker-group {
  display: flex;
  gap: 8px;
  justify-content: center;
  margin-top: 12px;
}
.carousel-item::scroll-marker {
  content: "";
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #333333;
  cursor: pointer;
}
.carousel-item::scroll-marker:target-current {
  background: #a855f7;
}

/* Prev/next buttons — browsers without support simply won't render them (progressive enhancement) */
.carousel::scroll-button(left) {
  content: "←";
}
.carousel::scroll-button(right) {
  content: "→";
}
.carousel::scroll-button(left),
.carousel::scroll-button(right) {
  position: absolute;
  color: #a855f7;
  cursor: pointer;
}

About this tool

The CSS Scroll Carousel Generator builds a fully functional, zero-JavaScript carousel using the CSS scroll-snap and scroll-marker APIs — scroll-snap-type/scroll-snap-align for the core scrolling-and-snapping behavior (broadly supported for years), plus ::scroll-marker, ::scroll-marker-group, and ::scroll-button() for auto-generated dot navigation and prev/next buttons (newer, reaching Chrome/Edge 135+ and Safari 19+ support through 2026). The dots aren't manually positioned elements you'd have to keep in sync with the slide count — scroll-marker-group automatically generates one marker per scrollable child that declares a ::scroll-marker, and the :target-current pseudo-class lets you style whichever marker corresponds to the currently-snapped slide, entirely through CSS.

When to use it

  • →Building an image or testimonial carousel without shipping any carousel JavaScript library
  • →Adding accessible dot navigation to a scroll-snap gallery without manually tracking the active slide in JS
  • →Prototyping a product card carousel with prev/next buttons that degrade gracefully on unsupported browsers
  • →Learning the ::scroll-marker / scroll-marker-group / ::scroll-button() syntax by example

Tips

  • ◆The core scrolling and snapping (scroll-snap-type, scroll-snap-align) works in every modern browser regardless of ::scroll-marker support — the generated dots and buttons are progressive enhancement, not a hard requirement for the carousel to function.
  • ◆Firefox has scroll-snap support but keeps ::scroll-marker/::scroll-button() behind a flag as of 2026 — test on your actual target browsers before relying on the generated dot navigation being visible everywhere.
  • ◆scroll-marker-group: before vs. after only changes where the dot row sits in the DOM's visual and tab order, not which element it's attached to — the generated markers always correspond to the direct scrollable children.

Frequently asked questions

Do I need any JavaScript at all for this carousel to work?

No — scrolling, snapping, dot navigation, and prev/next buttons are all pure CSS. The dots and buttons are clickable by nature (::scroll-marker and ::scroll-button() generate real interactive elements the browser handles), so there's genuinely no JavaScript carousel library needed.

What happens in a browser that doesn't support ::scroll-marker yet?

The carousel still scrolls and snaps normally via plain scroll-snap-type/scroll-snap-align — those have been well-supported for years. You simply won't see the generated dots or buttons; the content remains fully scrollable by swipe, wheel, or keyboard.

Can I style the 'current' dot differently from the others?

Yes — that's what the ::scroll-marker:target-current pseudo-class is for. It targets whichever generated marker corresponds to the slide currently snapped into view, letting you give it a different color or size without any scroll-position JavaScript.

Related tools

🥷 ToolNinja