sdxc

[ @sdxc/u/animation ]

timeline-scope

Applies the timeline-scope property, raising a named timeline's visibility to a common ancestor's subtree so animations outside the declaring element can resolve the name; the subtree still declares it.

import { timelineScope } from "@sdxc/u/animation";

Quick reference

Every documented call, beside the CSS it emits.

CallCSS
u.timelineScope("page-scroll")css({ timelineScope: "--page-scroll" })
u.timelineScope("page-scroll", "hero-reveal")css({ timelineScope: "--page-scroll, --hero-reveal" })

Examples

Basic example

Use timelineScope() on a common parent to let elements outside the scroll container use its named timeline. Scroll the box to fill the bar above it:

Paragraph 1. Scroll this box to drive the timeline.

Paragraph 2. Scroll this box to drive the timeline.

Paragraph 3. Scroll this box to drive the timeline.

Paragraph 4. Scroll this box to drive the timeline.

Paragraph 5. Scroll this box to drive the timeline.

Paragraph 6. Scroll this box to drive the timeline.

Paragraph 7. Scroll this box to drive the timeline.

Paragraph 8. Scroll this box to drive the timeline.

<div mix={[timelineScope("article"), vstack({ gap: 3, align: "stretch" })]}>
	<div
		mix={[
			bs(2),
			bg("brand.solid"),
			transformOrigin("left"),
			animation("progress", {
				keyframes: { from: { scale: "0 1" }, to: { scale: "1 1" } },
				duration: "auto",
				easing: "linear",
				timeline: "--article",
			}),
		]}
	/>
	<div mix={[scrollTimelineName("article"), scroll("y"), bs(40), ...]}>
		<div mix={[...]}>
			{[1, 2, 3, 4, 5, 6, 7, 8].map((n) => (
				<p mix={[...]}>Paragraph {n}. Scroll this box to drive the timeline.</p>
			))}
		</div>
	</div>
</div>

Scoping several names

Pass several names, like timelineScope("left", "right"), to raise more than one timeline. Scroll either box to fill its bar:

Paragraph 1. Scroll this box to drive the timeline.

Paragraph 2. Scroll this box to drive the timeline.

Paragraph 3. Scroll this box to drive the timeline.

Paragraph 4. Scroll this box to drive the timeline.

Paragraph 5. Scroll this box to drive the timeline.

Paragraph 6. Scroll this box to drive the timeline.

Paragraph 1. Scroll this box to drive the timeline.

Paragraph 2. Scroll this box to drive the timeline.

Paragraph 3. Scroll this box to drive the timeline.

Paragraph 4. Scroll this box to drive the timeline.

Paragraph 5. Scroll this box to drive the timeline.

Paragraph 6. Scroll this box to drive the timeline.

<div
	mix={[timelineScope("left", "right"), grid(), gridTemplate({ columns: "1fr 1fr" }), gap(3)]}
>
	{["left", "right"].map((name) => (
		<div
			mix={[
				bs(2),
				bg("brand.solid"),
				transformOrigin("left"),
				animation(`progress-${name}`, {
					keyframes: { from: { scale: "0 1" }, to: { scale: "1 1" } },
					duration: "auto",
					easing: "linear",
					timeline: `--${name}`,
				}),
			]}
		/>
	))}
	{["left", "right"].map((name) => (
		<div mix={[scrollTimelineName(name), scroll("y"), bs(40), ...]}>
			<div mix={[...]}>
				{[1, 2, 3, 4, 5, 6].map((n) => (
					<p mix={[...]}>Paragraph {n}. Scroll this box to drive the timeline.</p>
				))}
			</div>
		</div>
	))}
</div>

Applying on a state

Wrap the call in a state utility to scope it to one selector.

u.hover(u.timelineScope("page-scroll"))

Responsive design

Wrap the call in a container query to scope it to one width.

u.at("md", u.timelineScope("page-scroll"))

Signature

u.timelineScope(...names: string[])

Written by Sergio Xalambrí. Follow @sergiodxa for new packages, or sponsor the work.