Github

Carousel

.carousel, .carousel-track, .carousel-slide, then .carousel-prev / .carousel-next on outline icon buttons. The track is overflow-x: auto with scroll-snap-type: x mandatory. There is no wrapper. A few lines of scrollBy on the page (slide width) are enough — pasting that twice is painful, so the helper lives next to the markup.

Preview

Code

<script>
	let track;
	function step(dir) {
		track.scrollBy({ left: dir * track.clientWidth, behavior: 'smooth' });
	}
</script>

<div class="carousel">
	<div class="carousel-track" bind:this={track} role="region" aria-label="Example slides">
		<div class="carousel-slide">
			<p>Slide 1 of 3</p>
			<p>Native overflow and CSS scroll-snap.</p>
		</div>
		<div class="carousel-slide">
			<p>Slide 2 of 3</p>
			<p>Prev and next call scrollBy.</p>
		</div>
		<div class="carousel-slide">
			<p>Slide 3 of 3</p>
			<p>One full slide at a time.</p>
		</div>
	</div>
	<button class="button button-outline button-icon carousel-prev" type="button" aria-label="Previous slide" onclick={() => step(-1)}>
		<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
			<path d="m15 18-6-6 6-6"></path>
		</svg>
	</button>
	<button class="button button-outline button-icon carousel-next" type="button" aria-label="Next slide" onclick={() => step(1)}>
		<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
			<path d="m9 18 6-6-6-6"></path>
		</svg>
	</button>
</div>