Fylgja Carousel

A carousel is a horizontal, scroll-snapping strip of slides. The base version is pure CSS: a few Fylgja utilities turn a row of items into a snapping slider that works with touch, trackpad, and scrollbar, no JavaScript.

Preview

A deer in a misty forest An orange fox in the grass A cat lounging in the sun
<div class="grid-cols grid-flow snap scroll-x scroll-mask gap" style="--grid-cols: 1.15; --snap-align: center">
	<img class="rounded w-full object-cover" style="--size-y: 240px" src="/images/placeholders/deer-wide.webp" alt="A deer in a misty forest" width="740" height="240" />
	<img class="rounded w-full object-cover" style="--size-y: 240px" src="/images/placeholders/fox.webp" alt="An orange fox in the grass" width="500" height="300" />
	<img class="rounded w-full object-cover" style="--size-y: 240px" src="/images/placeholders/cat.webp" alt="A cat lounging in the sun" width="500" height="300" />
</div>

Usage

  • grid-cols grid-flow lays the slides out in equal columns flowing horizontally; --grid-cols sets how many are visible (1.15 peeks the next one).
  • snap adds scroll-snap so each slide clicks into place, scroll-x makes the strip scrollable, and gap spaces the slides.
  • Size each slide’s media as you like (here w-full object-cover with a fixed --size-y height and a rounded corner).

Enhanced with Snap Slider

For keyboard navigation and prev/next buttons, upgrade the same markup with the Snap Slider. Install and register it (as a custom element or an Alpine plugin) from the Snap Slider docs, which also cover the pager and other options. The CSS slider still works if the JS never loads, and it ships as a custom element (recommended) and as an Alpine.js component, so use whichever fits your stack.

Put the data-prev / data-next buttons before the track. The custom element needs role="region" and an aria-label to name the region; the Alpine version uses a <section> with an aria-label, which is a region landmark on its own.

<snap-slider role="region" aria-label="Featured photos">
	<div class="flex gap-sm" style="--my: 0 var(--size-3)">
		<button type="button" data-prev aria-label="Previous slide">
			<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>
		</button>
		<button type="button" data-next aria-label="Next slide">
			<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>
		</button>
	</div>
	<div data-track class="grid-cols grid-flow snap scroll-x gap" style="--grid-cols: 1.15">
		<img class="rounded w-full object-cover" style="--size-y: 240px" src="/images/placeholders/deer-wide.webp" alt="A deer in a misty forest" width="740" height="240" />
		<img class="rounded w-full object-cover" style="--size-y: 240px" src="/images/placeholders/fox.webp" alt="An orange fox in the grass" width="500" height="300" />
		<img class="rounded w-full object-cover" style="--size-y: 240px" src="/images/placeholders/cat.webp" alt="A cat lounging in the sun" width="500" height="300" />
	</div>
</snap-slider>

Accessibility

  • The strip is scrollable by keyboard; the Snap Slider adds arrow-key navigation between slides.
  • Name the slider region with an aria-label (or a heading referenced by aria-labelledby) so it is announced as a distinct region.
  • Give each slide’s image a descriptive alt.
  • If the carousel auto-advances, provide a pause control and do not trap essential content behind the auto-play.

FAQ

Does the carousel need JavaScript?

No. The scroll-snap slider is pure CSS via Fylgja utilities. The Snap Slider package is optional and only adds a pager, keyboard navigation, and prev/next buttons on top.

How do I change how many slides are visible?

Set --grid-cols. Use 1 for one slide at a time, a value like 1.15 to peek the next slide, or 2 and up to show several at once.

How do I install and set up the Snap Slider?

The Snap Slider ships only JavaScript, so install the package and register the custom element or Alpine plugin. The example markup here leaves that out on purpose; the install and setup steps live in the Snap Slider docs.