Skip to content

fix: re-measure after a parent's CSS transition ends (#227) - #250

Merged
justin-schroeder merged 4 commits into
masterfrom
fix/parent-transform-coords
Nov 5, 2026
Merged

justin-schroeder merged 4 commits into
masterfrom
fix/parent-transform-coords

Conversation

@justin-schroeder

@justin-schroeder justin-schroeder commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

Closes #227 🧭

Bug: when a parent (or an ancestor) moves via a CSS transition — a menu sliding in with transform — nothing tells auto-animate, so children keep their old cached positions and the next change animates them from where they used to be (45–51px off in the review harness, ~300px in the repro).

Fix (small, on purpose): listen for transitionend/animationend (capture, document-level). When one ends on an animated parent or one of its ancestors, re-measure that parent and its children (updatePos). Those events only fire for CSS transitions/animations, never for our own WAAPI animations. Mutation handling is untouched — exactly master's.

Why not realign at mutation time? Two independent reviews of that approach found regressions (percentage transforms like centered modals translate(-50%,-50%), bottom-anchored toasts, bordered kanban columns, re-parenting). Telling a transform move from a reflow move at mutation time isn't reliable; re-measuring after the transform finishes is.

Verification: both reviewers' master-vs-PR harnesses (37 scenarios × chromium/webkit) are identical to master except cssTransitionSettled (45–51px → 0) and ±2px timing noise. Mutating during the slide is unchanged (same as master; noted as a follow-up). New e2e: #227 sibling + removed-child cases with a real CSS transition (fail on master), plus centered/bottom-anchored guards. Full e2e 76 ✓ / 8 skipped.

Also: e2e console checks ignore GitHub API 403s from the docs' star counter (rate limits made unrelated tests fail locally).

Children's cached positions went stale when their parent moved without them
reflowing (e.g. a menu sliding in with transform/translate), so the next
mutation animated siblings — and the parent itself — from where they used to
be, and removed children faded out at their old on-screen spot.

Each child now remembers where its parent was when it was measured; on a
mutation the cached coordinates are shifted by however far the parent moved
since. The parent's own cached position is updated too (keeping its size),
unless it's nested inside another animated parent.

Closes #227
@vercel

vercel Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
auto-animate Error Error Nov 4, 2026 11:09pm UTC

Request Review

Measuring the parent after a change couldn't tell a transform move from a
move caused by the change itself, so bottom-anchored (toast) and centered
(modal) parents made existing children jump, and a same-render reflow
snapped instead of sliding.

Now only the parent's transform displacement counts: its on-screen position
minus its layout position and ancestor scroll offsets (scroll above a fixed
ancestor ignored). Reflow, anchoring and resizing change both equally, so they
no longer shift anything. The parent's own cached position is shifted by the
same transform delta instead of being overwritten. Nothing is recorded or
realigned while auto-animate itself animates the parent or an ancestor, so its
own FLIP transforms aren't mistaken for user transforms. The parent's
displacement is cached per task (one measurement for many children).

Adds regression tests for bottom-anchored and centered parents.
@justin-schroeder

Copy link
Copy Markdown
Member Author

Review round 🔍 → reworked: only transform movement realigns now (on-screen pos − layout pos − scroll), so reflow/anchoring/resizing don't shift anything; no realign while auto-animate's own FLIP runs on the parent or an ancestor; the parent's cached position is shifted, not overwritten.

Against the reviewer's 14-scenario harness (chromium + webkit), the PR now matches master everywhere — centered, bottom-fixed (toasts), same-tick reflow, nested, scroll boxes, kanban, disable/enable, plugins — except the #227 transform case, which it fixes. New regression tests for bottom-anchored + centered parents (fail on the previous version). Full e2e 76 ✓ / 8 skipped.

Not in 0.10.1 (shipping tomorrow) — targeting the next patch after another look.

Replaces the mutation-time realignment (two reviews found it regressed
percentage transforms, centered/anchored parents, bordered nested parents
and re-parenting) with a much smaller fix for #227: when a CSS transition or
animation ends on an animated parent or one of its ancestors, re-measure the
parent and its children. Mutation handling is back to exactly master's.

Also ignore GitHub API rate-limit errors in the e2e console checks (the docs
fetch the star count).

Closes #227
@justin-schroeder justin-schroeder changed the title fix: account for a parent moved by a CSS transform (#227) fix: re-measure after a parent's CSS transition ends (#227) Nov 4, 2026
…ions

Review of #250: a dark-mode toggle with `* { transition: color, ... }` fired
~80k transitionend events, each re-arming updatePos for every child. Skip
paint-only properties (they can't move anything) and flush queued parents
once per frame. Also read the target via composedPath() so it works inside
shadow roots.
@justin-schroeder

Copy link
Copy Markdown
Member Author

Final review: SHIP ✅ — and applied its perf suggestion: paint-only transitions (color, background, opacity, shadows, filters…) are ignored, and re-measures are coalesced to one pass per frame (a * { transition: color } dark-mode toggle fired ~80k events). Target is read via composedPath() so shadow DOM works too. e2e 76 ✓. Merging right after 0.10.1 is tagged → ships in 0.10.2.

Known limits (documented, not regressions): JS/WAAPI-driven slides (GSAP, Motion, element.animate) fire no transitionend so they still wait for the 2s poll; mutating during the slide is unchanged.

@justin-schroeder
justin-schroeder merged commit 29ee7a9 into master Nov 5, 2026
2 of 3 checks passed
@justin-schroeder

Copy link
Copy Markdown
Member Author

Released in 0.10.2 🚀

This branch had an error being deployed

1 failed deployment
Preview — 1b15da88 Deployed Nov 4, 2026 by vercel[bot]
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.

Incorrect child animation when parent element is CSS-translated before auto-animate ref is animated

1 participant