
Fade-in-on-scroll is built the same way almost everywhere. Set the element to zero opacity, watch it with an IntersectionObserver, and transition it in when it crosses the viewport.
It works until the observer does not fire.
When that happens
- A tab that is open but never composited, because the window is minimised or occluded.
- Embedded browser views and screenshot services that never paint.
- Crawlers and link-preview bots.
In each case the elements stay at zero opacity forever. The page loads, returns a perfectly healthy 200, contains all its markup — and shows nothing.
We caught this by creating an observer by hand on a fully visible element and watching it never call back.
The fix is two lines of defensiveness
First, reveal anything already inside the viewport synchronously on mount, measured with a bounding rect rather than waiting for a callback. Above-the-fold content should never animate in anyway.
Second, set a failsafe timer. If the observer has delivered nothing after a second and a half, show everything regardless.
A page that fails to animate is a small disappointment. A page that fails to appear is a bug — and the failure mode should always be the first one.