You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a TextMorph container transitions to an empty string and the user types a new character quickly (NOT immediately) the container sometimes permanently freezes at width: 0px; height: 0px.
Render a TextMorph inside an input overlay (transparent <input> + morphing text overlay).
Type a value, e.g. "100".
Hold backspace to clear the field to "".
Type a new character, slightly before fade animation ends (may need a few tries to trigger it).
Example (near the end stucked in weird position because container dimensions are stuck at 0);
demo-torph.mp4
Root cause
transitionContainerSize in animate.ts manages the width/height CSS transition on the torph-root element. When text becomes empty it animates the container shut, setting style.width and style.height to "0px", and stores a cancel callback in the module-level pendingCleanup:
pendingCleanup=()=>{element.removeEventListener("transitionend",onEnd);clearTimeout(fallbackTimer);pendingCleanup=null;// ⚠️ does NOT reset style.width / style.height};
CSS transitions are committed at frame boundaries, not synchronously during script execution. If a new keystroke arrives before the first transition frame renders, element.offsetWidth (forced reflow) returns 0 — the CSS target value — rather than the animated-from value.
The next call to transitionContainerSize then:
Calls pendingCleanup() — removes the transitionend listener and clears the fallback timer, without resetting style.width or style.height.
Hits the early-return guard because oldWidth === 0 || oldHeight === 0.
Returns, leaving style.width = "0px" and style.height = "0px" permanently on the element.
The normal completion path (cleanup()) does reset both properties to "auto", but it is never reached because pendingCleanup() already removed the listener and cleared the timer.
Fix
Reset style.width and style.height to "auto" in the early-return branch, so the container is always unblocked when source dimensions are unknown:
exportfunctiontransitionContainerSize(element: HTMLElement,oldWidth: number,oldHeight: number,duration: number,onComplete?: ()=>void,){if(pendingCleanup){pendingCleanup();pendingCleanup=null;}if(oldWidth===0||oldHeight===0){element.style.width="auto";// ← add these two lineselement.style.height="auto";// ←return;}// ... rest unchanged}
This ensures that even when transitionContainerSize exits early, any previously-pinned "0px" inline styles are cleared and the container reverts to natural content sizing.
Version:
0.0.9Description
When a
TextMorphcontainer transitions to an empty string and the user types a new character quickly (NOT immediately) the container sometimes permanently freezes atwidth: 0px; height: 0px.The computed styles on the stuck element:
Steps to reproduce
TextMorphinside an input overlay (transparent<input>+ morphing text overlay)."100"."".Example (near the end stucked in weird position because container dimensions are stuck at 0);
demo-torph.mp4
Root cause
transitionContainerSizeinanimate.tsmanages the width/height CSS transition on thetorph-rootelement. When text becomes empty it animates the container shut, settingstyle.widthandstyle.heightto"0px", and stores a cancel callback in the module-levelpendingCleanup:CSS transitions are committed at frame boundaries, not synchronously during script execution. If a new keystroke arrives before the first transition frame renders,
element.offsetWidth(forced reflow) returns0— the CSS target value — rather than the animated-from value.The next call to
transitionContainerSizethen:pendingCleanup()— removes thetransitionendlistener and clears the fallback timer, without resettingstyle.widthorstyle.height.oldWidth === 0 || oldHeight === 0.style.width = "0px"andstyle.height = "0px"permanently on the element.The normal completion path (
cleanup()) does reset both properties to"auto", but it is never reached becausependingCleanup()already removed the listener and cleared the timer.Fix
Reset
style.widthandstyle.heightto"auto"in the early-return branch, so the container is always unblocked when source dimensions are unknown:This ensures that even when
transitionContainerSizeexits early, any previously-pinned"0px"inline styles are cleared and the container reverts to natural content sizing.The corresponding
patch-packagefixes the issue.