Skip to content

Fix SVG zero-duration opacity animations - #3781

Merged
mattgperry merged 3 commits into
mainfrom
fix/3779-svg-duration-zero-independent
Aug 5, 2026
Merged

Fix SVG zero-duration opacity animations#3781
mattgperry merged 3 commits into
mainfrom
fix/3779-svg-duration-zero-independent

Conversation

@mattgperry

Copy link
Copy Markdown
Collaborator

Summary

  • render SVG opacity through CSS so WAAPI completion and instant JS updates write to the same channel
  • add a Cypress regression based on the issue's foreignObject reproduction
  • update SVG WAAPI assertions to reflect opacity's CSS rendering

Fixes #3779

Test plan

  • yarn build
  • yarn test (all 7 tasks; 96 Framer Motion suites and 809 tests)
  • reproduced the stale style.opacity = 0 before the fix in Chromium
  • verified fade-out and zero-duration restoration in Chromium with React 18 and React 19
  • targeted ESLint (no errors) and Prettier checks
  • Cypress CLI: repository-pinned Cypress 4 Electron cannot start on macOS 26 (bad option: --no-sandbox / --smoke-test)
  • repository-wide yarn lint: blocked by 52 pre-existing errors in unrelated dev examples/tests

Made with Cursor

Align JS and WAAPI SVG opacity updates so instant transitions cannot leave a stale inline style.

Co-authored-by: Cursor <cursoragent@cursor.com>
@mattgperry

Copy link
Copy Markdown
Collaborator Author

Follow-up verification correction: the Cypress launch failure was caused by ELECTRON_RUN_AS_NODE in the local Cursor environment, not by macOS/Cypress incompatibility. Running with that variable unset succeeds:

  • React 18: waapi-svg-zero-duration.ts — 1 passing
  • React 19: waapi-svg-zero-duration.ts — 1 passing

The unchecked Cypress item in the original PR description is therefore superseded by these passing runs.

@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown

Greptile Summary

This PR moves non-root SVG opacity rendering from presentation attributes to inline CSS so native animation completion and immediate updates write through the same channel.

  • Adds a foreignObject regression fixture and Cypress test for fade-out followed by zero-duration restoration.
  • Updates existing SVG WAAPI assertions to inspect inline opacity styles.
  • The regression test should additionally prove that it exercises native WAAPI rather than the JavaScript fallback.

Confidence Score: 4/5

The implementation appears safe to merge, with a non-blocking gap in whether the new regression test actually exercises the native WAAPI path.

The production change consistently routes non-root SVG opacity through CSS, but the new test's computed-style assertions can also pass under the JavaScript fallback and therefore do not fully guard the WAAPI-specific regression.

Files Needing Attention: packages/framer-motion/cypress/integration/waapi-svg-zero-duration.ts

Important Files Changed

Filename Overview
packages/motion-dom/src/render/svg/utils/build-attrs.ts Moves opacity from SVG attributes to CSS styles consistently with the intended WAAPI rendering channel; no concrete runtime regression was established.
packages/framer-motion/cypress/integration/waapi-svg-zero-duration.ts Covers the reported visual sequence but does not distinguish native WAAPI execution from the JavaScript fallback.
packages/framer-motion/cypress/integration/waapi-svg.ts Updates assertions to match the new CSS opacity output while retaining explicit native-animation capability checks.
dev/react/src/tests/waapi-svg-zero-duration.tsx Adds a focused foreignObject fixture reproducing fade-out and instant opacity restoration.

Reviews (1): Last reviewed commit: "Fix #3779: keep SVG opacity rendering in..." | Re-trigger Greptile

Comment on lines +7 to +10
.get("#chip")
.then(([$chip]: any) => {
expect(getComputedStyle($chip).opacity).to.equal("0")
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 WAAPI path remains unverified

The test checks only the final computed opacity, which the JavaScript fallback also produces. It can therefore pass without exercising the SVG WAAPI completion channel and will not reliably guard the reported native-animation regression.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

mattgperry and others added 2 commits August 5, 2026 06:57
Move transform, opacity, and motion-path properties through one shared style-routing loop.

Co-authored-by: Cursor <cursoragent@cursor.com>
Read shared SVG style properties from computed CSS with an attribute fallback, and cover svgEffect plus instant transform restoration.

Co-authored-by: Cursor <cursoragent@cursor.com>
@mattgperry
mattgperry merged commit b2bc637 into main Aug 5, 2026
5 checks passed
@mattgperry
mattgperry deleted the fix/3779-svg-duration-zero-independent branch August 5, 2026 11:12
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.

[BUG] Animating SVG elements may leave inline styles and SVG attributes out-of-sync (regression in 12.43.0)

1 participant