Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the scale CSS transformation function not affect margin?

I have a div that contains several other divs and elements.

Now I want to use transform: scale(n)

but when I do use it, everything looks fine, except that the margins of inner divs are kept the same size But the inner elements width are changed

enter image description here


enter image description here

As you can see the size of the div is cut by half... but the margin is the same :/

For example code, you have a div inside a parent. This child div has margin top of 100px

if I change the scale of the parent, the margin of the child stays the same but the size not. You can see that by the point the child is not moving while changing scales.

.holder {
  background: pink;
  transform: scale(0.5);
}

#son {
  margin-top: 100px;
  padding: 20px;
  background: green;
}
<div class="holder">
  <div id="son">
    dontcare
  </div>
</div>
like image 402
Tzook Bar Noy Avatar asked Feb 08 '18 09:02

Tzook Bar Noy


2 Answers

tl;dr

Transforms are relative to the reference box which is set by the transform-box property which defaults to border-box. The border box is bounded by the border. As a result the scale transformation will scale everything within and including the border (border, padding, content box).

Long answer

I'm going to start my answer off with the following example:

button {
  padding: 10px;
  margin: 10px;
}

.scaled {
  transform: scale(2);
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
</head>
<body>
  <button class="default">Button</button>
  <button class="scaled">Button</button>
</body>
</html>

There are two buttons. Both are given 10px padding and 10px margin (as well as default browser styles). The second is scaled by a factor of 2.

In Firefox 58 the first button renders with the following properties:

Screenshot of box model view in Firefox DevTools of first button

We'll focus on the properties and values of the horizontal axis:

        34.7167   // Content box width
        10        // Padding left
        10        // Padding right
         7        // Border left
         7     +  // Border right
    ------------
        68.7167
    ------------

        10         // Margin left
        10         // Margin right

If you total everything but the margin you'll get the element width. The margin is excluded. This is due to the browser defining the element box as everything inside and including the border. This behaviour is specified by the box-sizing rule set to border-box.

So how does this play into the scale transform function not scaling the margin?

Transformations occur relative to a reference box. The reference box is defined by the transform-box property which by default has the value border-box. As a result the border, padding and content box dimensions will be scaled by 2 while the margin is not.

This behaviour can be observed in the second button, which is scaled:

Screenshot of box model view in Firefox DevTools of second button

Again, looking at the properties and values of the horizontal axis:

    34.7167 * 2  =   69.4334   // Content box width
    10      * 2  =   20        // Padding left
    10      * 2  =   20        // Padding right
     7      * 2  =   14        // Border left
     7      * 2  =   14     +  // Border right
-----------------------------
    68.7167 * 2  =  137.4334
-----------------------------

    10                         // Margin left
    10                         // Margin right

Further reading

  • CSS Transforms Module Level 1 specification, specifically:
    • The definition for the user coordinate system or local coordinate system
    • The Transform Rendering Model
    • Transform reference box: the transform-box property
  • MDN documentation on box-sizing
  • MDN documentation on transform-box
like image 181
Wing Avatar answered Nov 19 '22 08:11

Wing


Your first screenshot seems to show an issue with transform-orginin which by default is 50% 50% 0 (the center of the element). Change it to top left and your element will be scaled for the top-left and thus the margin will decrease.


Concerning the code you shared, this is not related to scale but your are facing a margin-collapsing issue. The margin of the son is collapsed with the margin of its parent and the same thing happen until this one is collapsed with the body margin. In other words the margin no more belong to the scaled element.

Here is your intial code with the margin-collpasing:

body {
  background:linear-gradient(to bottom,#000 50%,transparent 0) 0 0/100% 100px;
}

.holder {
  animation:anim 3s infinite linear;
  transform-origin:top;
}

#son {
  margin-top: 100px;
  padding: 20px;
  background: green;
}

@keyframes anim {
  from {
    transform:scale(1);
  }
  to {
    transform:scale(0.3);
  }

}
<div class="holder">
  <div id="son">
    dontcare
  </div>
</div>

And the same code if we remove the margin-collapsing by adding a small padding:

body {
  background:linear-gradient(to bottom,#000 50%,transparent 0) 0 0/100% 100px;
}

.holder {
  padding-top:1px;
  animation:anim 3s infinite linear;
  transform-origin:top;
}

#son {
  margin-top: 100px;
  padding: 20px;
  background: green;
}

@keyframes anim {
  from {
    transform:scale(1);
  }
  to {
    transform:scale(0.3);
  }

}
<div class="holder">
  <div id="son">
    dontcare
  </div>
</div>

You can clearly see a difference between both of them. In the second example the margin is considered with the scale unlike the first one.


Here is some usefull links for more details:

CSS margin terror; Margin adds space outside parent element

https://stackoverflow.com/a/9519933/8620333

What is the point of CSS collapsing margins?

https://css-tricks.com/what-you-should-know-about-collapsing-margins/

like image 24
Temani Afif Avatar answered Nov 19 '22 09:11

Temani Afif