Force Flattening When Ancestor Has Opacity Sample

Available in Chrome 53+ | View on GitHub | Browse Samples

Background

"Flattening" is the process of drawing 3D-positioned children into the parent's rendering plane. HTML elements flatten by default, but can prevent this by applying the transform-style: preserve-3d CSS rule. This only has an effect if children are actually positioned in 3D.

Flattening is required/strongly desired, however, if elements have style which require filter-like rendering or clipping. Flattening is already forced for a number of the situations mentioned in the specification draft as "grouping properties".

This particular change is motivated by the simplifications it yields for how 3D-positioned elements with opacity are composited. Currently, opacity is the only post-processing effect which does not force flattening. Without this change, the browser is forced to apply opacity individually to elements positioned in 3D space rather than everything as a group.

Visualizing the Change

The images below are screenshots illustrating the difference in the expected rendering before and after the change introduced in Chrome 53. You can compare it to the Live Output section, which shows what your current browser actually renders.

Before Chrome 53

After Chrome 53

Live Output

The relative layering and blending of each of the colored boxes below will vary depending on whether the browser supports the old or new behavior.

HTML Snippet

<div id="outer-container">
  <div id="inner-container">
    <div id="first-inner-child"></div>
    <div id="second-inner-child"></div>
  </div>
  <div id="outer-child"></div>
</div>

CSS Snippet

#outer-container {
  transform-style: preserve-3d;
}

#inner-container {
  background: red;
  height: 100px;
  opacity: 0.5;
  transform-style: preserve-3d;
  transform: translate3d(0, 0, 30px);
  width: 100px;
}

#first-inner-child {
  background: green;
  height: 100px;
  transform: translate3d(50px, 0, -20px);
  width: 100px;
}

#second-inner-child {
  background: black;
  height: 80px;
  transform: translate3d(35px, -60px, -30px);
  width: 80px;
}

#outer-child {
  background: blue;
  height: 100px;
  transform: translate3d(25px, -50px, 20px);
  width: 100px;
}