Skip to main content
That is the whole integration. The browser drives the animation from the scroll position, on the compositor, so it does not compete with your main thread the way an IntersectionObserver reveal does.
This module is included in tailmotion/css and also available on its own as tailmotion/scroll.css — 12.1 KB raw, 4.0 KB gzipped. Every rule sits inside @supports (animation-timeline: view()), so in a browser without support not one declaration applies.

The vocabulary

Ranges

A view timeline runs from “this element starts entering the scrollport” to “it has left”. Which slice of that you animate over is the whole design decision.
Set --tm-scroll-range directly for anything more specific:
The default range starts once the subject is properly on screen and finishes before it reaches the middle. Running a reveal across the full entry range means the reader watches text resolve while trying to read it — the animation finishes at the exact moment their eye arrives, which is the worst possible timing.

Progress bar

Linked to scroll(root block), so it tracks the document. It scales from the inline-start edge and mirrors in right-to-left contexts. You size and color the bar; TailMotion scales it.

Restraint

Two rules this module follows, and you should too:
  • No large parallax. tm-scroll-slide-block travels twice --tm-distance and no further. Long parallax travel on body content makes text arrive somewhere the eye did not expect and measurably hurts reading.
  • No continuous decorative movement. Every class here settles at a final state and stays there. Nothing loops while the reader is trying to read.

Support and fallback

Everything sits inside @supports (animation-timeline: view()). Where that is false, not one declaration applies and the section renders as ordinary, fully visible content.
This is the difference between a CSS reveal and an observer-based one. An observer-based reveal has to hide the content first and then hope its JavaScript runs. If the script fails, the bundle 404s, or the browser is old, the reader gets a blank page. A CSS reveal that is not supported is simply a page.
Check the current status on caniuse.

Reduced motion

A progress-linked animation ignores animation-duration, so the library-wide 1ms reset cannot stop these. They are switched off by name instead:
Each element then renders at its own authored state — visible, in place, fully legible. tm-scroll-progress shows its complete state rather than tracking the scroll. If a permanently full bar would misinform your readers, hide it as well:
This is the one place TailMotion’s reduced-motion behaviour is a judgement call rather than a mechanical rule. The library’s convention is “land on the final state”, and for a progress bar the final state is full. Naming the trade-off here rather than picking silently is the point.

Verifying it works

Scroll-driven animations are attached as real Animation objects, so you can inspect them:
If getAnimations() returns an empty array, the browser has no support and the @supports guard did its job.