plans/issues/issue-2488.md
Executor instructions: This is a VERIFY-FIXED plan. The goal is to determine whether commits
801a699a5+a4df97a6c(Feb 2026, ResizeObserver constraint recalculation) already fixed this report, using a reconstructed repro. Follow steps in order; honor STOP conditions; update this issue's row inplans/issues/README.md.Drift check (run first):
gh api repos/motiondivision/motion/issues/2488 --jq .state→open.git diff --stat 42bfbe3ed..HEAD -- packages/framer-motion/src/gestures/drag/— ifVisualElementDragControls.tschanged, re-verify excerpts below.
42bfbe3ed, 2026-06-11Report (Jan 2024, framer-motion v10-era): with ref-based dragConstraints,
animation states that change the element's width make the effective
constraints differ from the specified ones. The CodeSandbox
(framer-motion-drag-reset-attempt-forked-pcthq9) is Cloudflare-blocked, so
the repro must be reconstructed from the description: a draggable box whose
width changes via animation states, inside a ref constraint container.
Since the report, the drag system gained exactly the machinery this bug was missing:
801a699a5 (2026-02-01) "fix(drag): Update constraints when draggable
element or container resizes" — adds ResizeObserver on both the draggable
and the constraint container.a4df97a6c (2026-02-03) "fix(drag): Fix resize observer setup and
constraint recalculation".Current code (verified at 42bfbe3ed,
packages/framer-motion/src/gestures/drag/VisualElementDragControls.ts):
startResizeObservers (lines 803–814) observes element + constraints
container and calls scalePositionWithinConstraints() (lines 589–659), which
strips the transform, re-measures layout, resets this.constraints = false
and re-resolves. So a width change from an animation state should now trigger
a full constraint recalculation.
This plan verifies that with a Cypress repro. If it reproduces anyway, it escalates (likely culprits listed in STOP conditions).
| Purpose | Command | Expected |
|---|---|---|
| Build | yarn build (repo root) | exit 0 |
| Cypress React 18 | CLAUDE.md recipe (dev/react Vite on random port) with --spec cypress/integration/drag-constraints-width-state.ts | see steps |
| Cypress React 19 | CLAUDE.md recipe (dev/react-19 + cypress.react-19.json) | same result as 18 |
In scope:
dev/react/src/tests/drag-constraints-width-state.tsx (create)packages/framer-motion/cypress/integration/drag-constraints-width-state.ts (create)Out of scope (without escalation): any change under
packages/framer-motion/src/ or packages/motion-dom/src/.
Test page dev/react/src/tests/drag-constraints-width-state.tsx (exported
App), modeled on dev/react/src/tests/drag-ref-constraints.tsx:
ref'd div, position: relative, 400×400, at a
known page position.motion.div with drag, dragConstraints={containerRef},
dragElastic={0}, dragMomentum={false}, and animate toggling between
two states via a button: state A { width: 100 }, state B { width: 250 }
(use transition={{ duration: 0.1 }} so the resize settles fast).Spec packages/framer-motion/cypress/integration/drag-constraints-width-state.ts
(model pointer sequences on
cypress/integration/drag-ref-constraints-element-resize.ts, which tests the
adjacent scenario):
.wait(300) for resize + observer.pointerdown →
two pointermoves to e.g. 800,800 with force: true → pointerup)..then() that the box's getBoundingClientRect().right
equals the container's .right (±5) and .bottom equals container
.bottom (±5) — i.e. full travel, no shrunken range.left/top align with container (±5).Verify: spec runs to completion on React 18 (pass or fail is the
finding, not an error). Capture output with tail -60.
test/issue-2488-drag-constraints-width-state and open a PR labeling it a
regression test for #2488, body noting it verifies the fix landed in
801a699a5/a4df97a6c. (Per repo policy on speculative coverage: this is
acceptable here because the test exercises a landed fix — it is a
regression gate, not happy-path padding. If the maintainer prefers no new
test, the PR can be closed; the verification finding stands.)gh api repos/motiondivision/motion/issues/2488/comments -f body=...:
explain it was verified fixed by the ResizeObserver constraint
recalculation shipped Feb 2026 (v12.23+), name the commits, ask the
reporter to retest on latest v12.plans/issues/README.md is
APPROVED:
gh api -X PATCH repos/motiondivision/motion/issues/2488 -f state=closed -f state_reason=completed.
Otherwise leave open, mark row BLOCKED (awaiting close approval).Do not fix in this plan. Report findings with the failing assertion values. For the report, note the prime suspects:
resolveConstraints() caches ref constraints (if (!this.constraints),
VisualElementDragControls.ts:351-354) — a width animation in progress
at drag start measures a transient width.skipFirstCall wrapper (lines 792–801) — the first resize after observer
setup is deliberately ignored; a width change racing observer setup would
be missed.scalePositionWithinConstraints re-measures with the transform stripped
(lines 622–627) — interplay with an in-flight width animation.plans/issues/README.md row updatedplans/issues/README.md cross-cutting facts) and a flaky verdict is not
evidence either way.whileTap width changes): note the uncertainty in the issue
comment and ask the reporter to confirm against latest v12 — do NOT iterate
more than one extra page variant (repo rule: no repro → no fix).