overflow + snap
overflow-x: auto + scroll-snap-type: x mandatory and scroll-snap-align on each card.
Carousel with scroll markers and scroll buttons
overflow-x: auto + scroll-snap-type: x mandatory and scroll-snap-align on each card.
::scroll-button(left/right) creates native arrows; the browser disables them on its own (:disabled).
::scroll-marker-group + ::scroll-marker and :target-current for dots, 100% styleable.
A scrollable container + pseudo-elements = accessible carousel, with keyboard and no libraries.
With scroll-snap-type: x mandatory the browser forces snapping to the nearest card on release.
Tab moves to the carousel, arrow keys move between cards. All handled by the browser, no JS.
scroll-padding-left adjusts the snap offset so the first card isn't flush against the edge.
If ::scroll-button isn't supported, scrolling still works. Only the arrows are lost.
Drag or use the arrows/dots. In browsers without support you still get a fully usable horizontal scroll.
.carousel {
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
scroll-marker-group: after; /* dots nativos */
}
.carousel .card {
scroll-snap-align: start; /* cada card hace snap */
width: 280px;
}
/* flechas nativas — el navegador las deshabilita solo */
.carousel::scroll-button(left) { content: "‹"; }
.carousel::scroll-button(right) { content: "›"; }
/* dots nativos — :target-current marca el activo */
.carousel::scroll-marker { width: 10px; height: 10px; }
.carousel::scroll-marker:target-current {
background: var(--amber-500);
transform: scale(1.3);
}Many of these APIs are recent and not yet in every browser (see the pill on each section). The nice part is they all degrade gracefully: the carousel still scrolls, the accordion opens/closes, the textarea works — just without the animated "plus".