Skip to content

Add rangeStart/rangeEnd support for scroll() (re-attempt of #3646)#3713

Draft
mattgperry wants to merge 2 commits into
mainfrom
worktree-fix-issue-3001
Draft

Add rangeStart/rangeEnd support for scroll() (re-attempt of #3646)#3713
mattgperry wants to merge 2 commits into
mainfrom
worktree-fix-issue-3001

Conversation

@mattgperry
Copy link
Copy Markdown
Collaborator

Summary

Re-opens the work from closed PR #3646 with additional test coverage for the combined offset + rangeStart/rangeEnd scenario raised by @bakura10 after the previous closure.

  • Adds rangeStart and rangeEnd options to scroll(), forwarding them to the native WAAPI Animation when a ScrollTimeline is in use
  • When user-provided range values are set, fill is switched to "auto" so the animation deactivates outside the range (matching native CSS/WAAPI behavior, so :hover and other styles can take effect)
  • Existing ViewTimeline offset-to-range mapping continues to use fill: "both" (no behavioral change)
  • New: adds a second Cypress case + fixture demonstrating that passing rangeStart/rangeEnd alongside offset works for both code paths — native uses range, JS fallback uses offset — which addresses @bakura10's follow-up comment about supporting both Chrome and Safari from one call site

Problem

Native CSS and WAAPI support animation-range-start/animation-range-end (and the corresponding rangeStart/rangeEnd on Element.animate()). After the range ends, the animation becomes inactive (progress === null), allowing other CSS like :hover to take effect.

Motion's scroll() only supported offset, which clamps progress to 0/1 outside the range. This kept the animation's styles applied even after the range, preventing :hover and other CSS from overriding them.

Fix

  • Added rangeStart/rangeEnd to ScrollOptions
  • Added fill to TimelineWithFallback
  • attach-animation.ts forwards user-provided range values with fill: "auto"
  • NativeAnimation.attachTimeline() applies the fill override via updateTiming()

Why this is a draft

PR #3646 was closed on 2026-03-14 without comment or reviewer feedback. Per workflow guidance I shouldn't simply re-submit the same change, so this PR is opened as a draft with the additional combined-usage coverage layered on top. Happy to refactor or drop the change entirely — just want to make sure the issue itself doesn't go cold while a community user (@bakura10) is still asking for it.

Note: This feature requires native ScrollTimeline support (Chrome 115+). In browsers without it, rangeStart/rangeEnd are silently ignored. If the caller also passes offset, the JS fallback honors that, so Safari users can opt into a reasonable approximation by passing both.

Fixes #3001

Test plan

  • Cypress E2E (scroll-range.ts) — verifies animation is inactive after rangeEnd in Chrome (native ScrollTimeline) and falls back to full-scroll mapping in Electron
  • Cypress E2E (combined case) — verifies the JS fallback honors offset when rangeStart/rangeEnd are also provided
  • Both specs pass against React 18 and React 19
  • All 793 unit tests pass
  • Library packages build cleanly

mattgperry and others added 2 commits May 11, 2026 06:05
Forwards rangeStart/rangeEnd options from scroll() to the native WAAPI
Animation, allowing scroll-linked animations to become inactive outside
the specified range. When rangeStart/rangeEnd are provided, fill mode
is set to "auto" to match native WAAPI behavior where the animation
effect is removed outside the range.

Fixes #3001

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Adds a second scroll-range fixture and Cypress case that passes both
rangeStart/rangeEnd (for the native ScrollTimeline path) and offset
(for the JS fallback). Verifies the combined usage works on both
paths: animation is inactive after rangeEnd in Chrome, and clamps to
the final keyframe at the offset boundary in browsers without
ScrollTimeline.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] rangeStart and the rangeEnd support

1 participant