TL;DR: You can’t use a CSS transform on any element that has fixed positioning applied to it or its children because reasons below:

So this a quick one about a bug I discovered while working on a site.

In my testing thus far, the best performing animations on older and slower hardware is CSS transform values being transitioned. Especially useful for the simple stuff like elements unfolding or sliding onto screen. The project I’m currently working on has a lot of DOM events going on so I thought that CSS transforms was to be my savior when it came to performance on mobile devices. Little did I know that setting a transform value to any element effectively disables any other position value but relative.

What happens is, a transformed element creates a containing block, for both itself and its children, which is then set to position:relative;

This means that ANY element within the transformed element that is set to position:fixed;, is no longer fixed to the viewport (as you would expect) but rather its newly created containing block as a result of the transform property.

For instance, if you have a fixed header within a section, and then push that section in any direction using a transform, that header is no longer fixed. Technically fixed to its parent. But its parent is not the viewport.

This bug has been around for a while from my research, but unfortunately it has not been addressed yet. Its also pretty obscure so I thought I’d make a post about it, hoping that someone else see’s it before spending hours scratching their heads. I am yet to find an effective solution, but here’s what I’ve tried and why its not good enough for me:

Set the element position as absolute

Does absolutely nothing. Because the absolute position is relative to its parent, which is no longer the viewport on transform.

Provide a transform: translateY value

This kind of works as the end result is what you want, but the animation between is horrible. Further than that, you need to write javascript to run on the scroll event, to find the scroll value, and then write that to the DOM. We may as well be animating margins.

Remove the fixed element out of the parent being transformed

This solves half the problem, where the fixed element retains its fixture to the viewport, but it also makes things pretty messy. Also, we design systems, not pages. So unless you can be 100% sure that there will NEVER be a position:fixed; or position:absolute; element within your transformed parent element. You setting yourself up for a critical bug in the future. One that will have to be fixed on a Friday at 16:45pm because “it should be a simple css tweak and deploy”. So much for read-only Fridays.

Here is a codepen that demonstrates this bug: