Skip to content

Fix MarqueeWall reset-on-stop flicker: freeze in place, don't snap to origin#1498

Merged
michaelassraf merged 1 commit into
mainfrom
claude/marquee-animation-reset-flicker-61e046
Jul 19, 2026
Merged

Fix MarqueeWall reset-on-stop flicker: freeze in place, don't snap to origin#1498
michaelassraf merged 1 commit into
mainfrom
claude/marquee-animation-reset-flicker-61e046

Conversation

@michaelassraf

Copy link
Copy Markdown
Contributor

Problem

On stepped/animated marquee surfaces, when the marquee stops after already running the track snaps back to its initial position — a one-frame flicker. Reproducible on company-hub /?slide=8 (the AutoFix slide): its core wall animates only on step 1 (animate={s === 1}); advancing past step 1 flips animate false and the track jumps to origin.

Root cause

<MarqueeWall> collapsed two distinct concepts into one marqueeActive flag:

const marqueeActive = mode === 'animated' && animate && !reducedMotion && overflows

That single flag gated both the structure (clone copy + transform driver mounted) and the rAF run state. So when the per-beat animate run-gate flipped false (a stepped slide advancing, or the deck-wide WCAG pause button), the transform-clear effect fired track.style.transform = '' and the layout effect reset posRef = 0 — snapping to origin and unmounting the clone. Notably the existing nearViewport gate already did the right thing (stop the rAF without clearing the transform), so pausing just needed to behave the same way.

Fix

Split the flag:

  • marqueeMounted = mode === 'animated' && !reducedMotion && overflows — structural. Owns the clone copy, seam gap, will-change, transform-clear, and posRef reset. Tears down only on a genuine static transition (content now fits, or reduced-motion).
  • marqueeActive = marqueeMounted && animate — run-gate. Owns only the engine's active.

Toggling the run-gate (step-gate / deck pause / off-screen) now just stops the rAF and freezes the track exactly where it was; the persisted velocity envelope means resume is at cruise, not a 0→speed ramp — identical to the nearViewport contract.

Side benefits

  • The WCAG pause button previously snapped every running wall to origin; it now freezes cleanly.
  • FLIP-morph capture stays accurate for trackId chip walls — a paused wall keeps its real offset in the registry instead of zeroing it.

Scope & safety

One file (src/components/ui/marquee-wall.tsx). The other MarqueeWall consumers are unaffected: QuickActionWall is a thin wrapper, and CardsStrip drives the engine on a real scroller that never resets scrollLeft on stop. A full audit of the lib, the company-hub deck (FLIP morphs, scroll-pinning, step reveals), and the rest of the hub found no other instance of this reset-on-stop pattern — the deck was already deliberately built to freeze/settle in place.

Verification

  • tsc --noEmit clean.
  • Manual device/preview check on company-hub /?slide=8 recommended before the hub pins the release (per-repo convention).

Follow-up

Lib-only change → after merge + release, bump the @flamingo-stack/openframe-frontend-core pin in multi-platform-hub to pull it into company-hub.

🤖 Generated with Claude Code

… origin [preview:none]

Split the single `marqueeActive` flag into a structural gate and a run-gate.
Previously one flag controlled both "is this a marquee (clone + transform
driver mounted)" and "is the rAF running", so when the per-beat `animate`
run-gate flipped false (a stepped deck slide advancing past a wall's beat, or
the deck-wide WCAG pause), an effect cleared `track.style.transform` and reset
`posRef` to 0 — snapping the track back to its origin. That snap read as a
one-frame flicker (visible on company-hub /?slide=8).

Now `marqueeMounted` (mode animated + overflows + motion allowed) owns the
clone copy, seam gap, will-change, transform-clear, and position reset — it
only tears down on a GENUINE static transition (content now fits or
reduced-motion). `marqueeActive = marqueeMounted && animate` owns only the
engine's rAF. Toggling the run-gate now just stops the loop and freezes the
track exactly where it was, resuming at cruise via the persisted velocity
envelope — identical to how the existing `nearViewport` gate already behaved.
Also fixes the WCAG pause button, which previously snapped every running wall
to origin, and keeps FLIP-morph capture accurate for trackId chip walls.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@michaelassraf, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8dd6532b-668a-4dec-b647-be6130e61da7

📥 Commits

Reviewing files that changed from the base of the PR and between 8e76cf1 and e796160.

📒 Files selected for processing (1)
  • openframe-frontend-core/src/components/ui/marquee-wall.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/marquee-animation-reset-flicker-61e046

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@michaelassraf
michaelassraf merged commit dae3088 into main Jul 19, 2026
6 checks passed
@michaelassraf
michaelassraf deleted the claude/marquee-animation-reset-flicker-61e046 branch July 19, 2026 00:11
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.

1 participant