PULSENOVA

JOURNAL

EVERY ENTRY CARRIES ITS NUMBERS

A transform breaks every fixed element inside it

A finished GSAP animation leaves a transform on the element, even when the value is the identity matrix. That leftover broke every fixed element inside it after each page change.

1 IDENTITY MATRIX · 2 BROKEN PINS · 2400 PX DRIFT

The rule of the browser

An element with position fixed is normally positioned against the viewport. That stops being true as soon as any ancestor carries a transform. The transformed ancestor becomes the containing block. The fixed element is then positioned against that ancestor instead of the viewport.

This is standard browser behaviour and it is well documented. It is still one of the easiest things to walk into, because nothing about it is visible.

How it showed up

After every page change our pinned horizontal scroll stopped pinning. A flying image landed in the wrong place. Both worked on a fresh load. Both broke on a click.

Why the identity matrix is the trap

The animation on the container had already finished. GSAP leaves the transform property on the element when it finishes, even when the final value is the identity matrix, which changes nothing you can see.

The browser does not care whether the matrix does anything. The presence of the property is enough to create a containing block. That is what makes this hard to spot. Nothing looks transformed. Nothing has moved. The element carries a transform anyway.

The fix

Clear the property instead of resetting its value. We now clear position, top, left, right and transform on the container in our page reset. Setting the transform back to none by hand is not the same thing, because the property is still there.

What to check on your own site

Open the console and read the computed transform of every ancestor of anything you pinned. If one of them returns a matrix instead of none, you have found it. Check this after a page change and not on a fresh load, because that is when the leftover appears.