8

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>
Bart
  • 527
  • 1
  • 7
  • 16
Tzook Bar Noy
  • 11,337
  • 14
  • 51
  • 82

3 Answers3

6

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

Wing
  • 8,438
  • 4
  • 37
  • 46
  • sorry but how this is related ?, it's well explained btw ... but if you check his example you will see that margin belong to a child element and not the element it self ... so the issue is why the inner margin which should be included in the transformation is not being considered – Temani Afif Feb 08 '18 at 15:00
  • by the way check the devtools and use a very small scale and you will see that maring is also considered – Temani Afif Feb 08 '18 at 15:09
  • @TemaniAfif: Ahh I didn't see the transform rule declared on the parent. Thanks for raising. However I believe my answer still stands because "Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent" and the local coordinate system "has its origin at the top-left of a reference box specified by the `transform-box` property." Quoted from https://drafts.csswg.org/css-transforms/#transform-rendering. – Wing Feb 08 '18 at 15:48
  • try it and see the effect :) if you scale the container of both element the inner margin will decrease. He has two main issue check below my answer for more detail ;) – Temani Afif Feb 08 '18 at 15:48
  • If you follow the link you'll see that `transform-box` is an experimental feature and can't be changed on older browsers. – Andris Dec 17 '18 at 15:08
3

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/

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
0

Transformations do affect the visual rendering, but have no affect on the CSS layout other than affecting overflow.

from W3.org《The Transform Rendering Model》 Standard

and demo here:

console.log('getComputedStyle:height: ' + getComputedStyle(box).height);
console.log('getComputedStyle:width: ' + getComputedStyle(box).width);
#box {
  width: 100px;
  height: 100px;
  background: pink;
  transform: scale(2, 2);
}
<div id="box">
  box
</div>

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

you can use the getComputedStyle to find that the inner elements width didn't change;

xianshenglu
  • 4,943
  • 3
  • 17
  • 34
  • 1
    @Bart that is ,the actual size is still the same as before you set,i try to find the documents – xianshenglu Feb 08 '18 at 10:01
  • but visually they change and this is the purpose of the question ... what you meant that nothing is changing but he said that the width is changing and not the marigin so he's talking about the visual i guess – Temani Afif Feb 08 '18 at 10:22
  • 1
    i guess he think that the transformations change the width,actually didn't,everything stay where they are and just change the visual rendering which means you thought something change like width,actually didn't – xianshenglu Feb 08 '18 at 10:28
  • yes but we are talking about the visual width .. we all know the initial won't change ... but as you can see in his screenshot visual it changing and thus the question – Temani Afif Feb 08 '18 at 10:29
  • of couse the visual change,,,,you set the transform: scale and then surprised by visual the change. i don't understand if there is anything to feel surprised. – xianshenglu Feb 08 '18 at 10:34
  • he is not surprised :) he simply doesn't understand why the width changed visually and not the margin ... which is not clear by the way as for me both are changing visually. – Temani Afif Feb 08 '18 at 10:35