Layout Advanced

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.

Old 15+ lines
1/* JS: Swiper.js carousel with nav + dots */
2import Swiper from 'swiper';
3new Swiper('.carousel', {
4  navigation: {
5    nextEl: '.next',
6    prevEl: '.prev',
7  },
8  pagination: { el: '.dots' },
9});
10/* + custom CSS for buttons, dots, states */
Modern
14 lines
1.carousel::scroll-button(left) {
2  content: "⬅" / "Scroll left";
3}
4.carousel::scroll-button(right) {
5  content: "➡" / "Scroll right";
6}
7
8.carousel { scroll-marker-group: after; }
9
10.carousel li::scroll-marker {
11  content: '';
12  width: 10px; height: 10px;
13  border-radius: 50%;
14}
scroll buttons and markers, no JS
::scroll-button() + ::scroll-marker()

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.

Browser Support
72%
Chrome Edge Firefox 🔜 Safari 🔜
View on caniuse.com →
Lines Saved
15+ → 14
Zero JS, native a11y
Old Approach
JS carousel lib
Swiper.js, Slick, Flickity
Modern Approach
CSS pseudo-elements
::scroll-button, ::scroll-marker

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.

ESC