2

Can anyone explain this behavior?

I have three nested elements: a container div, a subcontainer div and a "shape/image" div. The markup is like below:

<html>
  <div class="container">
    <div class="subcontainer">
      <div class="shape"></div>
    </div>
  </div>
</html>

When I apply transform: translateZ() to the shape, obviously it will only work if one of the parents has some perspective applied. It doesn't need to be necessarily the direct parent, it can be the parent's parent div (the container). When the container has the perspective, the shape moves in the z direction fine. But, when the subcontainer (the parent) has something in the transform, other than unset, the translateZ() from the shape is fully ignored. Of course, applying perspective to the direct parent makes the translateZ() in the shape work, but I wanted to understand what in the parent transform blocks the perspective to be seen by the shape:

Below works:

.container{
  perspective: 1000px;
}

.subcontainer{
  transform: unset;     // <- or if this is not present at all
}

.shape{
  transform: translateZ(300px);
}

Below doesn't work:

.container{
  perspective: 1000px;
}

.subcontainer{
  transform: scale(1.001);
}

.shape{
  transform: translateZ(300px);    // <- this is fully ignored
}

Here is this example in codepen.

EDIT: In many docs I read that perspective must be a property set in the parent element. But MDN seems to show that it can in fact be set in the intended element itself, inside the transform rule. So, is perspective: 500px in the parent equivalent to transform: perspective(500px) in the child?

Adriano_Pinaffo
  • 1,429
  • 4
  • 23
  • 46

1 Answers1

3

From the specification:

By default, transformed elements do not create a 3D rendering context and create a flattened representation of their content. However, since it is useful to construct hierarchies of transformed objects that share a common 3-dimensional space, this flattening behavior may be overridden by specifying a value of preserve-3d for the transform-style property. This allows descendants of the transformed element to share the same 3D rendering context.

So simply add transform-style:preserve-3d to the subcontainer element and the perspective of the grand-parent will be respected:

.container{
  background: #f4f7be;
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
  perspective: 1000px;
}

.subcontainer{
  background: #baf7f6;
  width: 70%;
  height: 70%;
  display: flex;
  justify-content: center;
  align-items: center;
  transform: scale(1.001);
  transform-style:preserve-3d;
}

.shape{
  background: #555;
  width: 40%;
  height: 50%;
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  transform: translateZ(300px);
}
<html>
  <div class="container">
    <div class="subcontainer">
      <div class="shape"></div>
    </div>
  </div>
</html>

So, is perspective: 500px in the parent equivalent to transform: perspective(500px) in the child?

Not exactly equivalent because you need to consider other factors like the parent dimension, transform-origin, perspective-origin, etc

A trivial example:

.box {
  padding: 20px;
  border: 1px solid;
}

.box>div {
  width: 100px;
  height: 100px;
  background: red;
}
<div class="box" style="perspective:100px;">
  <div style="transform:translateZ(10px);"></div>
</div>

<div class="box">
  <div style="transform:perspective(100px) translateZ(10px);"></div>
</div>

The difference is due to the origin. In the first case, the perspective will use the center of the parent as origin (because perspective-origin is center by default). In the second case, the center of the element will be used as origin (because transform-origin is center by default)

Related questions for more detail and examples:

perspective and translateZ moves diagonally

Why perspective isn't giving the same result when some styles are updated?

CSS 3d transform doesn't work if perspective is set in the end of property

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • so, for the first one, isn't it weird that when the parent element has no transform specified the "3D features are preserved" even if you don't specify anything in the transform-style of the parent? – Adriano_Pinaffo Dec 21 '20 at 16:06
  • @Adriano_Pinaffo it's not that weird because transform-style doesn't really control the 3D feature. The 3D is controled by perspective (you can find the full detail here: https://drafts.csswg.org/css-transforms-2/#3d-transform-rendering) BUT in some cases the 3D context is disabled for some hierarchy (maybe for perf issue) and you have the ability to activate it again using transform-style. Like in your case where an element having transform will not propagate the 3D rendring context to its child element. – Temani Afif Dec 21 '20 at 16:21