demo · v132
Carousel Navigation
The ::scroll-button() pseudo-element adds declarative prev/next buttons directly to a scroll container — no JavaScript needed. These two carousels use ::scroll-button(inline-start) and ::scroll-button(inline-end) for automatic ← → navigation, and the buttons disable themselves when the scroll reaches the edge.
Checking ::scroll-button() support…
Card carousel (auto-buttons via CSS)
01
CSS Scroll Snap
scroll-snap-type + scroll-snap-align constrain the scroll positions to snap points.
02
::scroll-button()
Chrome 132 adds this pseudo-element to put navigation controls on the scroller itself.
03
No JavaScript
The browser handles scrolling, disabling, and ARIA labelling automatically.
04
Disabled state
When the scroll container is at its start, inline-start button gets disabled. Same at the end.
05
Scroll markers
Pair with ::scroll-marker-group and ::scroll-marker for accessible dot indicators.
06
All directions
Supports inline-start/end (← →) and block-start/end (↑ ↓) for vertical scrollers too.
Overlay-button style (full-height click targets)
🖼
🌄
🗺
📷
🎨
✏️
Before and after Chrome 132
Before — JavaScript required
// HTML
<div class="carousel">
<button id="prev">←</button>
... items ...
<button id="next">→</button>
</div>
// JS — ~30 lines of boilerplate
prevBtn.addEventListener('click', () => {
carousel.scrollBy({ left: -300 });
});
nextBtn.addEventListener('click', () => {
carousel.scrollBy({ left: 300 });
});
// + must manually disable buttons at edges
// + must keep buttons accessible
// + must handle resize events
Chrome 132 — pure CSS
// No extra HTML needed
<div class="carousel">
... items ...
</div>
/* CSS only — zero JS */
.carousel::scroll-button(inline-start),
.carousel::scroll-button(inline-end) {
display: flex;
width: 2.5rem;
height: 2.5rem;
background: white;
cursor: pointer;
}
.carousel::scroll-button(inline-start) {
content: "←";
}
.carousel::scroll-button(inline-end) {
content: "→";
}
/* Browser auto-disables at edges! */
/* The full ::scroll-button() direction set */
.scroller::scroll-button(inline-start) { content: "←"; } /* left */
.scroller::scroll-button(inline-end) { content: "→"; } /* right */
.scroller::scroll-button(block-start) { content: "↑"; } /* up */
.scroller::scroll-button(block-end) { content: "↓"; } /* down */
/* Auto-disable when at scroll edges */
.scroller::scroll-button(*):disabled {
opacity: 0.3;
cursor: not-allowed;
}
/* Style the active state */
.scroller::scroll-button(*):hover {
background: var(--bg-stone);
}