Fixing GSAP Overlay Flicker in CosmicInterestsSection (Next.js 15)
This post documents how I investigated and fixed a subtle flicker that appeared during the rollback phase of a long-scrolling animation in src/components/CosmicInterestsSection.tsx
.
The symptom: after the interest card animation completes, the dual‑color background occasionally flickered, sometimes even flashing the nav bar and making the cards briefly disappear.
What caused the flicker?
The issue came from a combination of compositing and stacking context churn:
- Nested alpha fades: Both the overlay container (
overlayRef
) and its child.black-overlay
were animating opacity during the tail/rollback. When a parent and child change alpha simultaneously, browsers can promote/demote layers and re-blend large surfaces frame-to-frame, which is prone to flicker. - Blend and blur under motion: Cards use gradient overlays with
mix-blend-overlay
, and the text background hadbackdrop-blur
. While these are beautiful, animating big areas on top at the same time increases the chances of composition “pops”. - Pin boundary thrash: Near the
ScrollTrigger
end, we were toggling overlay visibility and pointer events. Combined with smoothing and pinning, that quick toggling can cause a momentary reflow/repaint that looks like flashing.
How I fixed it
All changes were local to src/components/CosmicInterestsSection.tsx
and did not alter the high-level flow.
- Keep the parent overlay fully opaque: During the tail we stopped fading the overlay container and modulate only the child
.black-overlay
opacity. This prevents nested alpha composition conflicts. - Stabilize compositing: Promoted
.black-overlay
and the overlay container to their own GPU layers (translateZ(0)
,backface-visibility: hidden
,will-change
) and usedisolation: isolate
on.black-overlay
to keep blending predictable. - Reduce visibility thrash: Instead of toggling
visibility
at the boundary, we move the overlay offscreen (yPercent: 100
) and disable pointer events, plus a short debounce so we don’t flip states every frame when hovering near the end. - Lighten blur under load: The text background blur is kept very light in the initial phase and disabled during heavy composition moments to avoid GPU spikes.
- Z-index sanity: Lowered the overlay’s z-index so the nav always stays reliably above, eliminating header flashing.
Key snippets
Stop fading the parent; control child only
// Stage 5 tail: keep overlay fully opaque and visible
// src/components/CosmicInterestsSection.tsx (onUpdate tail)
gsap.set(overlayRef.current, { opacity: 1, visibility: "visible" });
// Modulate only the black overlay
const blackOverlay = overlayRef.current?.querySelector(".black-overlay");
if (blackOverlay) {
const blackAlpha = 1 - tailProgress * 0.15; // 1 -> 0.85
gsap.set(blackOverlay, { opacity: blackAlpha, ease: "none" });
}
Promote layers for stable composition
// .black-overlay is a dedicated compositing layer now
<div
className="black-overlay absolute inset-0 bg-black z-30 pointer-events-none"
style={{
opacity: 0,
willChange: "opacity",
contain: "paint",
backfaceVisibility: "hidden",
transform: "translateZ(0)",
isolation: "isolate",
}}
/>
Avoid visibility flipping at the boundary
// When ScrollTrigger deactivates, debounce hiding to avoid thrash
if (!overlayHideTimeoutRef.current) {
overlayHideTimeoutRef.current = window.setTimeout(() => {
gsap.set(overlayRef.current, {
yPercent: 100,
pointerEvents: "none",
opacity: 1,
ease: "none",
});
overlayHideTimeoutRef.current = null;
}, 80);
}
Results
- No more dual‑color flicker during rollback.
- Interest cards and the nav bar remain solid, with no flashes.
- Animation feel remains intact; only the rendering stability was improved.
If you’re animating complex overlays on top of blended/blurred backgrounds, favor controlling a single opacity source and keep the rest of the stack stable. Promote layers intentionally and avoid rapid visibility/pointer-events toggles near ScrollTrigger boundaries.
Appendix: Tools & Stack
- Next.js 15 (App Router), React 19
- GSAP + ScrollTrigger
- Tailwind CSS
- Hardware-accelerated compositing hints (
translateZ(0)
,will-change
,backface-visibility
)