Repository navigation
[scroll-animations-1] Bring back Scroll Offsets #7575
Description
Activity
Isn't it what we have
ScrollTimelinefor? Isn't that simply a use-case for that?
Or, if the author prefers usingViewTimeline, then it makes sense they have a revealing element that holds the timeline, and the header is simply the target of the animation. Which should be straightforward using the current API, or did I miss something and it's not?Ok, just checked and we did lose that ability on
SctollTimeline. We definitely need to have offset and duration defined by<length>s, same asViewTimelinecan define those using phases.
So @bramus I guess this was the intention here?I’ve given this some thought over the weekend, and could think of a few possible scenarios to tackle this:
-
Bring back the old
scroll-offsetsasscroll-timeline-offsetsThis seems like a straightforward thing, but is too limiting. For example:
body { scroll-timeline: body-tl block; scroll-timeline-offsets: 0 90vh; /* 👈 */ } @keyframes shrink { from { height: 100vh; } to { height: 10vh; } } #coverheader { position: sticky; top: 0; animation-name: shrink; animation-timeline: body-tl; }
By defining the offsets on the
ScrollTimelineitself, the entireScrollTimelineis limited to the range0-90vh. What if the authors want to animate on scroll outside of that range (e.g. from90vhto100%)? This is not possible with this approach, unless authors are able to define multipleScrollTimelines on the same element. -
Allow
scrollas a (faux) phase with<length-percentage>Following the way
ViewTimelinephases are defined, ascrollphase could be introduced. That way there still is 1 set of keyframes and 1ScrollTimeline. In the keyframes, authors can target several parts of the timeline by prefixing that phase to the keyframe offsets.body { scroll-timeline: body-tl block; } @keyframes shrink { scroll 0 { height: 100vh; } /* 👈 */ scroll 90vh { height: 10vh; } /* 👈 */ } #coverheader { position: sticky; top: 0; animation-name: shrink; animation-timeline: body-tl; }
Authors would need to explicitly target the (faux)
scrollphase if they want to use it with aScrollTimeline. Instead of accepting only<percentage>s as the keyframe offset,<length>s would now also be valid here.One of the cool things about the whole scroll-linked animations concept is that you can use regular keyframes without needing to target any phase. If wanted, maybe, it could be so that “no phase implies the
scrollphase” when used with aScrollTimeline?@keyframes shrink { 0 { height: 100vh; } /* When used linked to a ScrollTimeline, this would imply the scroll phase */ 90vh { height: 10vh; } /* When used with a DocumentTimeline animation, this would be invalid as it’s no <length> */ }
-
Introduce
animation-timelineoffsetsA third option would be to introduce the
scroll-offsetson theanimation-timelinelevel. There would be two properties:animation-timeline-startandanimation-timeline-end, with a<length-percentage>as their allowed value.body { scroll-timeline: body-tl block; } @keyframes shrink { from { height: 100vh; } to { height: 10vh; } } #coverheader { position: sticky; top: 0; animation-name: shrink; animation-timeline: body-tl; animation-timeline-offsets: 0 90vh; /* 👈 */ }
This approach keeps the ability to define 1
ScrollTimelineon a an element and also to target parts of it when linking an animation. However, authors would need to attach two animations/keyframes when targeting various offsets.#coverheader { animation-name: shrink, otheranimation; /* 👈 */ animation-timeline: body-tl, body-tl; /* 👈 */ animation-timeline-offsets: 0 90vh, 90vh 100%; /* 👈 */ }
One of the key things that
ViewTimelineintroduced, was the ability for authors to define 1 set of keyframes which can target all different phases if needed. This approach withanimation-timeline-offsetsdoes not offer this. -
Something else?
-
This feels very similar to issue #7296 though I like some of the suggestions to handle this in a way similar to the way enter/exit phases are handled for ViewTimeline (See #7044). Following that thread, I think rather than offsets we could use whatever we decide the new shorthand for specifying start and end delay to set the range of the animation, e.g.
#coverheader { animation-delay: 0 90vh, 90vh 100%; /* Same as setting startDelay to 0 and 90vh respectively and endDelay to 90vh and 0 respectively. */ }
Reacted by Yehonatan DanivYes, since we already decided, at least for now, that these are properties of the animation, and not of the timeline. So these should go, as @flackr said above, on the
animationproperty asstartDelayandduration/endDelay.Though this still makes @bramus's no. 2 suggestion valid, at least conceptually, with regard to the
@keyframesextension.The extra added phase would leave the path for future additions open. Say a
HoverTimelinewere to be added, then it’s only a matter of adding ahoverphase.When no explicit phase is defined in the keyframes, the phase could be implied from the usage: adding non-phased keyframes to a
ScrollTimeline? Use those “regular” keyframes. Adding keyframes that have ascrollphase to aScrollTimeline? Use thosescrollkeyframes, ignoring the non-phased keyframes.Reacted by Yehonatan Daniv and Nabil RedmannSome authors who have been playing around with the polyfill have raised similar issues, asking about offsets no longer being available.
- ScrollTimeline offsets are no longer respected after last update. flackr/scroll-timeline#64
- scrollOffset not working anymore flackr/scroll-timeline#76
The recent Motion One scroll library that enables JS-based Scroll-Linked Animations also offers this ability.
As @flackr suggested above, it could be achieved by having
animation-delay-startandanimation-delay-endaccept lengths as their value.animation-delay-start = [ <time> | <timeline-range-name> <percentage> | <length> ]animation-delay-end = [ <time> | <timeline-range-name> <percentage> | <length> ]
Reacted by Yehonatan DanivFWIW I was thinking allowing lengths or percentages for ranges would allow greater flexibility:
animation-delay: [ <time> | <timeline-range-name>? [<percentage> | <length>]]+
Then you could use calculations within ranges, e.g.
/* Starts 10px after entering until 10px before done entering. */ animation-delay: enter 10px enter calc(100% - 10px);
Reacted by Bramus, Yehonatan Daniv, Nabil Redmann, Szymon Nowicki and Eric A. MeyerOh yes, great tweak! I like it!
@fantasai Could we get input from you on this before the spec goes to FPWD? Would love to get this back in there before the spec moves forward.
FWIW I was thinking allowing lengths or percentages for ranges would allow greater flexibility:
Then you could use calculations within ranges, e.g.
I like the idea of adding length-percentages to
animation-durationandanimation-delay, particularly since viewport units could then be used.A few questions:
- When a length delay or duration is applied to a time-based animation, what does it resolve to?
- When a time delay is applied to a length-based animation, what happens? Do we create lag in the animation or ignore it or...?
- If an offset applied to a range is outside the range (outside 0%-100%), do we clamp it or not?
- When a length delay or duration is applied to a time-based animation, what does it resolve to?
- When a time delay is applied to a length-based animation, what happens? Do we create lag in the animation or ignore it or...?
I don't think this use-case is relevant to consider. As I suggested in #7944, time for delay and duration only make sense for time-based animations and length makes no sense there. And vice versa, if the animation has a timeline which isn't time-based, duration is irrelevant (since it's always 100%), and time for delay makes no sense.
So we could ignore delay in this case and only parse the ranges (or use default values for ranges).If an author wants to include both in
@keyframesto be used for either case they could write it as follows:@keyframes fade-in { 0%, enter 20% { opacity: 0; } 100%, enter 100% { opacity: 1; } }
19 remaining items
@bramus you are correct, the intent is to allow both with or without an animation-range as an alternative to a percentage on non-time based timelines. i.e. your first example should work.
As for the range end, I'm not sure we have a clear definition of what
exit 90vhmeans? Is that the same asexit 0% + 90vh?- 90vh? I think we need to resolve on #8054 to answer that.exit 90vhis equivalent toexit calc(0% + 90vh).@flackr great! But then I think we have an ambiguity here, see #7575 (comment). Should I open a new issue for this?
Sorry perhaps I'm just a bit slow today but can you explain the ambiguity? What's an example? Happy to see a new issue for that if you don't mind filing it.
Reacted by Yehonatan Daniv- added a commit that references this issue
on Mar 11, 2023 - addedClosed Accepted by CSSWG ResolutionChanges backed by a CSSWG resolution.Changes backed by a CSSWG resolution.and removed
on Mar 11, 2023 @fantasai I've added comments in the commit on specific lines.
- added a commit that references this issue
on Mar 13, 2023 @ydaniv Replied. OK to close the issue?
@fantasai yes, thanks!
- addedCommenter SatisfiedCommenter has indicated satisfaction with the resolution / edits.Commenter has indicated satisfaction with the resolution / edits.
on Mar 28, 2023 - added a commit that references this issue
on Apr 21, 2026
One of the things that did not make it into the scroll-animations rewrite is the ability to animate as a scroller scrolls for a fixed distance. This used to be covered by the scroll-offsets.
There are some use-cases, such as this cover page that transforms to a header bar as you scroll over a distance of
90vh.One could use use a
ViewTimelinehere, but not on the cover/header element itself because it is sticky and therefore neverexits. Instead, one would have to look at the succeeding element.This, however, is only possible if all of these apply:
enterThe first 2 conditions might not always be the case, but the author could work around that if they rearrange some things in their markup. The 3rd condition is not possible, since we decided in #7047 to have “timeline search look at preceding siblings and ancestors”.
Therefore I think we should reintroduce the Scroll Offsets.