CSS-Only Carousel Navigation with scroll-marker
Carousel navigation without a JavaScript library
Carousels used to need libraries like Swiper.js or Slick for navigation buttons and dot indicators. CSS scroll-button and scroll-marker pseudo-elements give you native, accessible carousel UI.
/* JS: Swiper.js carousel with nav + dots */import Swiper from 'swiper';new Swiper('.carousel', { navigation: { nextEl: '.next', prevEl: '.prev', } , pagination: { el: '.dots' } ,});/* + custom CSS for buttons, dots, states */ .carousel::scroll-button(left) { content: "⬅" / "Scroll left";}.carousel::scroll-button(right) { content: "➡" / "Scroll right";}.carousel { scroll-marker-group: after;}.carousel li::scroll-marker { content: ''; width: 10px; height: 10px; border-radius: 50%;} scroll markers Browser Support
This feature is not Baseline because it does not work in some of the most widely-used browsers.
Not ready for production without a fallback.
Native performance
Scroll buttons and markers are browser-generated pseudo-elements. No JS, no DOM manipulation, no resize observers.
Accessible by default
Buttons are focusable and auto-disable at scroll ends. Markers work as anchor links. Keyboard and screen reader friendly.
Drop the library
Swiper.js is ~40 KB. The CSS approach is zero bytes of JavaScript and fully stylable.
How it works
Building a carousel traditionally meant a JavaScript library: Swiper.js, Slick, or Flickity. These libraries create navigation buttons, dot indicators, handle scroll snap, and manage active states. That's a lot of JavaScript and custom CSS for what is fundamentally a scrolling UI.
CSS now provides two pseudo-elements for scroll containers. ::scroll-button(direction) creates prev/next buttons that scroll by ~85% of the container's visible area and auto-disable at the ends. ::scroll-marker on each item creates dot indicators grouped in a ::scroll-marker-group. Use the :target-current pseudo-class to style the active dot. Both are fully stylable with CSS.
role="region" and aria-label to the carousel container, and aria-label to each slide. Keyboard arrow navigation also requires JavaScript.