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.
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 */
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}
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.