Fix drag being blocked by variant transform string (#2807)#3728
Fix drag being blocked by variant transform string (#2807)#3728mattgperry wants to merge 1 commit into
Conversation
When a variant or style sets a literal `transform` string and drag (or another gesture driving x/y motion values) is active, `buildHTMLStyles` now prefers the result of `buildTransform` over the literal string so that the gesture-driven values are reflected on the element. Fixes #2807 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Greptile SummaryThis PR fixes a bug where a variant-supplied literal
Confidence Score: 4/5Safe to merge — the fix is narrow and well-targeted, with both a unit test and a Cypress E2E regression guard. The change is small and the logic is sound for the common x/y drag case. The only gap is that hasTranslate does not cover z, translateX, or translateY motion values — any element driven along those axes while a variant supplies a literal transform string would still silently lose its gesture translation. That gap is unlikely to affect real users today (drag drives x/y), but it leaves a latent inconsistency. All other existing tests are preserved. packages/motion-dom/src/render/html/utils/build-styles.ts — the hasTranslate condition could be broadened to cover z, translateX, translateY, and translateZ for full consistency. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[buildHTMLStyles called] --> B[Loop over latestValues]
B --> C{key in transformProps?}
C -->|yes| D[hasTransform = true, skip to style]
C -->|no, other| G[style key = value]
D --> H[continue loop]
G --> H
H --> B
B --> I[All keys processed]
I --> J{hasTranslate check — x or y in latestValues?}
J -->|yes, NEW PATH| K[Enter buildTransform block]
J -->|no| L{latestValues.transform absent?}
L -->|absent| K
L -->|present| M[Skip block — literal string stays]
K --> N{hasTransform or transformTemplate?}
N -->|yes| O[buildTransform runs — individual props win]
N -->|no| P{no transform string and stale style.transform?}
P -->|yes| Q[Reset style.transform to none]
P -->|no| R[No change]
|
| const hasTranslate = | ||
| latestValues.x !== undefined || latestValues.y !== undefined |
There was a problem hiding this comment.
The
hasTranslate guard only checks x and y, but transformPropOrder also exposes z, translateX, translateY, and translateZ as valid motion value keys. If an element uses z-axis gestures, or if a consumer drives translateX/translateY directly instead of x/y, a literal transform string in latestValues would still silently win over those individual props — reproducing the same bug as #2807. Extending the check to cover those aliases keeps the fix consistent across all translate axes.
| const hasTranslate = | |
| latestValues.x !== undefined || latestValues.y !== undefined | |
| const hasTranslate = | |
| latestValues.x !== undefined || | |
| latestValues.y !== undefined || | |
| latestValues.z !== undefined || | |
| latestValues.translateX !== undefined || | |
| latestValues.translateY !== undefined || | |
| latestValues.translateZ !== undefined |
Summary
buildHTMLStylespreviously preferred a literaltransformstring inlatestValuesover the computed transform from individual props. When a variant settransform: "..."and drag (or another gesture) was drivingx/ymotion values, the gesture's translations were silently dropped.buildTransformwin wheneverxoryare present inlatestValues, so a variant-set transform string no longer blocks drag.Bug
Reported in #2807. Repro from the issue:
The
nonevariant setstransformto a literal string. When drag updatedx/ymotion values,latestValues.transformwas still truthy, so the old check (if (!latestValues.transform)) skippedbuildTransformentirely andstyle.transformstayed at the variant string — drag did nothing.Fix
packages/motion-dom/src/render/html/utils/build-styles.ts: extend the gate so we still runbuildTransformwhenxoryis inlatestValues. The previous behaviour is preserved when neither translate axis is set, so auseMotionTemplate-based transform string with non-translate motion values (e.g.scale) still wins — the existing "Prioritises transform over independent transforms" test continues to pass.Test plan
build-styles.test.tsasserts that{ x: 50, transform: "translateX(100px)" }resolves totransform: "translateX(50px)". Fails without the fix.drag-variant-transform.tsreproduces the issue's scenario (dragControls+dragListener={false}+ varianttransformstring) and checks the element moves. Fails onmain(left=150— variant transform wins), passes with the fix.yarn test(798 tests) passes.Fixes #2807
🤖 Generated with Claude Code