Why does CSS transform influence Site width?

Viewed 152

I was always under the impression that CSS transforms are purely visual and don't influence the actual site layout in any way. I recently found a weird bug in my project, where an image which is transformed out of the viewport (to later fly in) changed the width of the page.

My coworkers and I are extremely confused by this. I provided an example below (also hosted here: https://transform-confusion.netlify.app/) that shows the phenomenon with a transform: scale(2) applied to a div completely changing the width of the browser.

Video of weird behaviour in action

Do I just misunderstand how CSS works, is this a weird browser bug or what is going on here?

Source code used here and in the video:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <style>
        body {
            border: 2px dotted black;
        }
      .scale {
        transform: scale(2);
        transform-origin: top left;
      }
      .scalable{
          background: lightblue;
          padding: 5px;
      }
    </style>
  </head>
  <body>
    <h1>This is a test</h1>
    <button>Toggle transform</button>
    <div class="scalable">
      Why does the Site start scrolling horizonally from a simple transform?
      <pre>transform: scale(2)</pre>
    </div>
    <script>
        const scalable = document.querySelector(".scalable");
        document.querySelector("button").addEventListener("click", () => {
            scalable.classList.toggle("scale");
        })
    </script>
  </body>
</html>

3 Answers

I was always under the impression, that CSS transforms are purely visual and don't influence the actual site layouting in any way

This is correct since transformation will not affect the position of any other element and the layout will remain the same.

What you are facing is related to overflow:

The scrollable overflow of a box is the set of things extending outside of that box’s padding edge for which a scrolling mechanism needs to be provided.

The scrollable overflow area is the non-rectangular region occupied by the scrollable overflow, and the scrollable overflow rectangle is the minimal rectangle whose axes are aligned to the box’s axes and that contains the scrollable overflow area.

.. the border boxes of all boxes for which it is the containing block and whose border boxes are positioned not wholly outside its block-start or inline-start padding edges, accounting for transforms by projecting each box onto the plane of the element that establishes its 3D rendering context. ref

So transform is a part of the Scrollable Overflow

You will find that some properties are part of the Ink Overflow and this one doesn't generate a scroll:

The ink overflow of a box is the part of that box and its contents that creates a visual effect outside of the box’s border box. Ink overflow is the overflow of painting effects defined to not affect layout or otherwise extend the scrollable overflow area, such as box shadows, border images, text decoration, overhanging glyphs (with negative side bearings, or with ascenders/descenders extending outside the em box), outlines, etc.

For example box-shadow will never create a scroll

.box {
  width:100px;
  height:100px;
  background:red;
  box-shadow:0 0 0 1000vmax blue;
}
<div class="box"></div>

I just found the important bit of info in the Specs. My misunderstanding was the overflow behaviour:

For elements whose layout is governed by the CSS box model, the transform property does not affect the flow of the content surrounding the transformed element. However, the extent of the overflow area takes into account transformed elements. This behavior is similar to what happens when elements are offset via relative positioning. Therefore, if the value of the overflow property is scroll or auto, scrollbars will appear as needed to see content that is transformed outside the visible area. Specifically, transforms can extend (but do not shrink) the size of the overflow area, which is computed as the union of the bounds of the elements before and after the application of transforms.

from https://drafts.csswg.org/css-transforms/#transform-property

Slightly late to the party, but here's my solution to the problem:

html {
    overflow-x: hidden;
}

Probably a bit hacky, but it works for me!

Related