DEV Community

Salil Naik
Salil Naik

Posted on

The uncanny relationship between position fixed and transform property.

TL;DR

An element with position:fixed is positioned relative to the document (the viewport) which acts as its containing block.

But, it will NOT always be relative to the document. When any element has transform, filter or perspective property, it acts as a containing block for all its descendants, including the elements whose position is set to fixed.

Now, the long story...

While I was working on enhancing the navigation bar of the
Polygon Wallet, I found myself stuck with a strange error. I spent almost a day trying to figure out the issue, consulted with the senior frontend engineers and also thought of making a separate Vue component as a workaround.

When I finally found the bug, I wouldn't shy away to say that my ego went crashing down, as it was a CSS bug, and my CSS skills are something I take pride in.

I was doing some changes to the code to align the dropdown menus to the cente of the navigation item (as shown in the picture below) for which I had to move the dropdown component inside the <li>. Doing this would allow me to use the CSS position and transform properties to align the dropdown exactly at the bottom-center of the navigation item.

Dropdown menu opens at the center

Till now, everything was working fine and nothing was unusual and unexpected.

Inside the dropdown menu, there were a bunch of notification items, which when clicked would open the modal component.

The components were nested something like this

<li>
  <span>Notifications</span>

  <dropdown-menu>
    <item>
      <modal />
    </item>

    <item>
      <modal />
    </item>
  </dropdown-menu>
</li>
Enter fullscreen mode Exit fullscreen mode

The modal component, which was supposed to open at the center of the screen when clicked on the item, would just behave weirdly and open inside the dropdown menu itself. I was sure that, any element with a fixed position would ignore all its ancestors and position itself relative to the document (the viewport or the root element). But that just didn't happen. The ancestor element, in this case, the dropdown menu was acting as a containing block for the modal component.

Modal opens in a weird way

And as you might have guessed, it was due to the transform property. Any element with transform, filter or perspective property will act as a containing block to all its descendants, including the elements whose position is set to fixed. Hence the modal, instead of positioning itself with respect to the document, positioned itself relative to the dropdown-menu component where I had used the transform property to align it exactly at the bottom-center of the navigation item. See CSS Transform Spec

How did I fix it?

Well, there is no fix for that, I just removed the transform property, gave a fixed width to all the dropdown components and achieved the same result with the help of the margin and position properties. But this uncanny relationship between position and transform property reminded me why CSS takes the crown as my favorite language even when I have a multi-paradigm language like JavaScript in my stack.

Modal opens as expected

Thank you!

If you want to learn about CSS positions, refer to
CSS Tricks
MDN Web Docs

Top comments (5)

Collapse
 
shridhar_kdev profile image
Shridhar Kamat

Really helpful! 😀

Collapse
 
salilnaik profile image
Salil Naik

I'm glad, Shridhar.

Collapse
 
peresnegro profile image
Porkopek

If you work with React, you can use a Portal

Collapse
 
arbs23 profile image
Afzalur Rahman Sabbir

@peresnegro vue already have Teleport

Collapse
 
arbs23 profile image
Afzalur Rahman Sabbir

Nice explanation! 💖