Skip to content

Fix SVG transform animations not applied without other SVG attributes#3632

Merged
mattgperry merged 1 commit intomainfrom
worktree-fix-issue-3081
Mar 12, 2026
Merged

Fix SVG transform animations not applied without other SVG attributes#3632
mattgperry merged 1 commit intomainfrom
worktree-fix-issue-3081

Conversation

@mattgperry
Copy link
Collaborator

Summary

  • Fixes isHTMLElement() to exclude SVG elements, which were incorrectly identified as HTML elements in Chrome/Edge due to offsetHeight existing on the SVGElement prototype (deprecated but still present)
  • This caused supportsBrowserAnimation() to route SVG transform animations through WAAPI instead of the JS animation path, bypassing the SVG render pipeline that sets transformBox: fill-box and transformOrigin
  • Adds E2E test reproducing the reported bug (SVG transform + stroke/fill animation without x/y)

Bug

In Chrome/Edge, SVG transform animations were not applied unless other SVG-specific attributes like x or y were also animated. The root cause was the isHTMLElement() utility using "offsetHeight" in element as a heuristic, which returns true for SVG elements in Chromium browsers where offsetHeight is still on the SVGElement prototype (though deprecated since Chrome 48). This incorrectly sent SVG transform animations through the WAAPI code path.

Fix

Add !("ownerSVGElement" in element) to the isHTMLElement() check. The ownerSVGElement property is present on all SVG elements (returning null for root <svg>, and the nearest <svg> ancestor for inner elements), making it a reliable cross-iframe way to exclude SVG elements.

Test plan

  • Unit test for isHTMLElement with inner SVG elements (<rect>, <g>)
  • Cypress E2E test reproducing the exact bug scenario (SVG transform + stroke/fill without x/y)
  • Tests pass on both React 18 and React 19
  • yarn build passes
  • yarn test passes (776 tests, 0 failures)

Fixes #3081

🤖 Generated with Claude Code

@greptile-apps
Copy link

greptile-apps bot commented Mar 11, 2026

Greptile Summary

This PR fixes a cross-browser bug where SVG elements (<rect>, <g>, etc.) were incorrectly identified as HTML elements in Chromium/Edge because the deprecated offsetHeight property is still present on the SVGElement prototype. This caused SVG transform animations to be routed through WAAPI instead of the JS render pipeline that sets transformBox: fill-box and transformOrigin, meaning SVG transforms were silently dropped unless other SVG-specific properties (like x/y) were also animated.

Key changes:

  • is-html-element.ts: Adds !("ownerSVGElement" in element) to the isHTMLElement() guard. This is the canonical, iframe-safe SVG detection heuristic and is already used by the existing isSVGElement utility — making the two functions now complementary and consistent.
  • Unit test: New test case verifies isHTMLElement() returns false for inner SVG elements (rect, g), complementing the pre-existing root <svg> test.
  • E2E test + dev fixture: A Cypress integration test reproduces the exact bug scenario (SVG transform + stroke/fill without x/y) and asserts the animated transform reaches the expected identity-matrix values.

Confidence Score: 5/5

  • This PR is safe to merge — the change is a minimal, well-scoped one-liner that closes a long-standing Chromium-specific regression with no risk to HTML element detection.
  • The fix is a single additional boolean guard in isHTMLElement() using ownerSVGElement, which is already the canonical SVG-detection heuristic in this codebase (isSVGElement.ts). The change is consistent with existing patterns, has zero impact on HTML elements (which don't have ownerSVGElement), and is covered by both unit and E2E tests.
  • No files require special attention.

Important Files Changed

Filename Overview
packages/motion-dom/src/utils/is-html-element.ts Adds !("ownerSVGElement" in element) check to correctly exclude SVG elements; fix is consistent with the existing isSVGElement utility which already uses the same ownerSVGElement heuristic.
packages/motion-dom/src/utils/tests/is-html-element.test.ts Adds unit test covering inner SVG elements (rect, g) which were the elements most impacted by the Chromium offsetHeight bug; complements the pre-existing root <svg> test.
packages/framer-motion/cypress/integration/svg-transform-animation.ts New E2E test reproducing the exact bug (#3081); validates transform is applied post-animation but only partially checks matrix values (scale diagonal), and does not assert stroke/fill colours on #svg-g.
dev/react/src/tests/svg-transform-animation.tsx New dev test component correctly sets up the bug-reproduction scenario (SVG transform + stroke/fill without x/y) for use by the Cypress E2E suite.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["Motion element (HTML or SVG)"] --> B["isHTMLElement(element)"]
    B --> C{"isObject(element)\nAND\n'offsetHeight' in element\nAND\nNOT 'ownerSVGElement' in element"}
    C -- "true (HTML element)" --> D["supportsBrowserAnimation()\n→ WAAPI path\n(hardware-accelerated)"]
    C -- "false (SVG element)" --> E["JS animation path\n(motion-dom render pipeline)"]
    E --> F["addStyleValue() in effects/style\nif !isHTMLElement → set transformBox: fill-box\nand transformOrigin"]
    F --> G["SVG transform applied\ncorrectly via style.transform"]
    D --> H["WAAPI animates element.animate()\n(bypasses SVG render pipeline)"]

    style C fill:#f9f,stroke:#333
    style E fill:#9f9,stroke:#333
    style H fill:#f99,stroke:#333
    style G fill:#9f9,stroke:#333
Loading

Last reviewed commit: a9d3af0

…#3081)

The isHTMLElement() check used "offsetHeight" in element, which returns
true for SVG elements in Chrome (where offsetHeight exists on SVGElement
prototype, though deprecated). This caused supportsBrowserAnimation() to
incorrectly route SVG transform animations through WAAPI instead of the
JS animation path, bypassing the SVG render pipeline that sets
transformBox and transformOrigin.

Add an exclusion for SVG elements via "ownerSVGElement" in element.

Fixes #3081

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@mattgperry mattgperry force-pushed the worktree-fix-issue-3081 branch from a9d3af0 to 86907d1 Compare March 12, 2026 05:07
@mattgperry mattgperry merged commit 67d86cf into main Mar 12, 2026
5 checks passed
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] SVG Transform animations are not applied if other SVG attributes are not also animated.

1 participant