Version selector

This demo has several versions:

  1. Using 1 shared ScrollTimeline
  2. Using a ViewTimeline per entry
🏠

About this demo

This demo sports a little carousel. As you scroll the carousel, the markers also indicate which element you are viewing.

The Code / Explanation

The carousel consists of two child elements: a .scroller with the content and a .markers element that holds all markers.

<div data-component="carousel">
	<ul class="entries" tabindex="0">
		<li><a name="carousel_01"><img src="../shared/img/matroshka-01.svg" alt="" title="" width="222" height="184" draggable="false"></a></li>
		…
	</ul>
	<ul class="markers">
		<li><a href="#carousel_01"><span class="sr-only">Photo 1</span></a></li>
		…ul>
</div>

The .scroller has its children laid out on a row, and has overflow-x: scroll applied on it. The .markers are position at the bottom of the carousel. Scroll snapping is used to make sure the carousel stops at a certain item instead of halfway.

The scroller is decorated with a scroll-timeline. The markers representing each item then use that value as their animation-timeline.

[data-component="carousel"] {
	.entries {
		scroll-timeline: --carousel x;
	}

	.markers a {
		animation-timeline: --carousel;
	}
}

Because timeline lookups only happen up the ancestor tree, the markers are not able to find the --carousel timeline – the .entries element is not part of their ancestor tree. To allow the markers to use that timeline, timeline-scope is used on the [data-component="carousel"] itself to hoist up the declared --carousel timeline, giving it a wider reach.

[data-component="carousel"] {
	timeline-scope: --carousel;
}

As there are 5 children, each child gets to run for 20% of the entire scroll range. To achieve this, each marker’s animation-range is set to only its specific sub-part. This is done using a --i custom property that indicates the element its child index.

.markers a {
	animation-range: calc((var(--i) - 1) * 20%) calc(var(--i) * 20% + 1px);
}

⚠️ Your browser does not support Scroll-driven Animations. Please use Chrome 115 or newer.