Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the content is not covered by the background of an overlapping element?

Tags:

Here is the situation:

body {    margin: 0;    background: pink;    color: #fff;  }    .box {    margin-top: 20px;    background: red;  }    .bottom {    text-align: right;    background: green;    animation: animate 2s infinite alternate linear;  }    @keyframes animate {    from {      margin-top: 10px;    }    to {      margin-top: -40px;    }  }
<div class="box">    some content  </div>  <div class="bottom">    other content  </div>

What's happening?

As you may see we have two div without any complex styling (simply background-color). I am making the second div to overlap the first one by applying a negative margin-top. I am expecting to see one completely overlapping the other but this is not the case. The second div is sliding between the content and the background of the first one and it's a strange behavior for me.

The animation has nothing to do here, I simply use it to better show the behavior. We can simply add negative margin without animation and we will have the same thing:

body {    margin: 0;    background: pink;    color: #fff;  }    .box {    margin-top: 20px;    background: red;  }    .bottom {    margin-top:-10px;    text-align: right;    background: green;  }
<div class="box">    some content  </div>  <div class="bottom">    other content  </div>

So my question is : why such behavior?


By the way, we all know that there is some tricky things with CSS that we don't suspect when we face them the first time (like margin-collapsing, background propagation from body to html, white space issue,etc...) but they are clearly explained somewhere and I hope to find an official resource where I can clearly understand this and not only get something like "Maybe this happen because...", "I suspect this related to...", "I think it have something to do with...",etc.


My opinion/explanation about this

I suppose content like text are more important than background and other visual styling so maybe when we have overlapping we place all the text at the top and all the other styling at the bottom, we decide about the order inside each group and then we print the result.

Here is a more complex example:

body {    margin: 0;    background: pink;    color: #fff;  }    div {    font-size: 39px;    line-height: 28px;    margin-bottom: -20px;    font-weight: bold;  }    body :nth-child(1) {    background: red;    border:3px solid brown;  }    body :nth-child(2) {    background: blue;    border:3px solid yellow;    color: #000;  }    body :nth-child(3) {    background: green;    border:3px solid orange;  }
<div>    some content  </div>  <div>    other content  </div>  <div>    more content  </div>

We can clearly see that the visual stack is as follow (starting from bottom to top):

  • Styling of the first div(background + border)
  • Styling of the second div(background + border)
  • Styling of the third div(background + border)
  • text content of the first div
  • text content of the second div
  • text content of the third div

Important notice: Before answering, please note that I am not looking for a fix to this or how to avoid this. By simply adding position:relative the behavior disappear and we can play with z-index to decide about stacking. Am looking to understand why such thing happen.

like image 980
Temani Afif Avatar asked Feb 11 '18 11:02

Temani Afif


People also ask

Why is my content overlapping in HTML?

The reason for the overlapping lines is quite simple: The line height is not sufficient to contain the text size. You have "hard-coded" the font-size via inline CSS but did not adjust the line-height , for instance.

Which position values can be used to make overlapping elements without overflowing content?

position: absolute; Note: Absolute positioned elements are removed from the normal flow, and can overlap elements.

How do you overlap a background-image in CSS?

CSS allows you to add multiple background images for an element, through the background-image property. The different background images are separated by commas, and the images are stacked on top of each other, where the first image is closest to the viewer.


2 Answers

WARNING: Reading the following information can affect your mental health.

The painting order for the descendants of an element generating a stacking context (see the z-index property) is:

  1. If the element is a root element:
    1. background color of element over the entire canvas.
    2. background image of element, over the entire canvas, anchored at the origin that would be used if it was painted for the root element.
  2. If the element is
    • a block, list-item, or other block equivalent:
      1. background color of element unless it is the root element.
      2. background image of element unless it is the root element.
      3. column rule of the element.
      4. border of element.
    • Otherwise, if the element is a block-level table:
      1. table backgrounds (color then image) unless it is the root element.
      2. column group backgrounds (color then image).
      3. column backgrounds (color then image).
      4. row group backgrounds (color then image).
      5. row backgrounds (color then image).
      6. cell backgrounds (color then image).
      7. cell column rule for multi-column.
      8. all table borders (in tree order for separated borders).
  3. Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order (most negative first) then tree order.
  4. For all its in-flow, non-positioned, block-level descendants in tree order:
    • If the element is a block, list-item, or other block equivalent:
      1. background color of element.
      2. background image of element.
      3. column rule of the element.
      4. border of element.
    • Otherwise, the element is a table:
      1. table backgrounds (color then image).
      2. column group backgrounds (color then image).
      3. column backgrounds (color then image).
      4. row group backgrounds (color then image).
      5. row backgrounds (color then image).
      6. cell backgrounds (color then image).
      7. cell column rule (multi-column).
      8. all table borders (in tree order for separated borders).
  5. All non-positioned floating descendants, in tree order. For each one of these, treat the element as if it created a new stacking context, but any positioned descendants and descendants which actually create a new stacking context are considered part of the parent stacking context, not this new one.
  6. If the element is an inline element that generates a stacking context, then:
    1. For each line box that the element is in:
      1. Jump to 7.2.1 for the box(es) of the element in that line box (in tree order).
  7. Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order:

    1. If the element is a block-level replaced element, then: the replaced content, atomically.
    2. Otherwise, for each line box of that element:

      1. For each box that is a child of that element, in that line box, in tree order:

        1. background color of element.
        2. background image of element.
        3. column rule of the element.
        4. border of element.
        5. For inline elements:
          1. For all the elements in-flow, non-positioned, inline-level children that are in this line box, and all runs of text inside the element that is on this line box, in tree order:
            1. If this is a run of text, then:
              1. any underlining affecting the text of the element, in tree order of the elements applying the underlining (such that the deepest element’s underlining, if any, is painted topmost and the root element’s underlining, if any, is drawn bottommost).
              2. any overlining affecting the text of the element, in tree order of the elements applying the overlining (such that the deepest element’s overlining, if any, is painted topmost and the root element’s overlining, if any, is drawn bottommost).
              3. the text
              4. any line-through affecting the text of the element, in tree order of the elements applying the line-through (such that the deepest element’s line-through, if any, is painted topmost and the root element’s line-through, if any, is drawn bottommost).
            2. Otherwise, jump to 7.2.1 for that element
        6. For inline-block and inline-table elements:
          1. For each one of these, treat the element as if it created a new stacking context, but any positioned descendants and descendants which actually create a new stacking context are considered part of the parent stacking context, not this new one.
        7. For inline-level replaced elements:
          1. the replaced content, atomically.
        8. Optionally, the outline of the element (see 10 below).

        Note, some of the boxes may have been generated by line splitting or the Unicode bidirectional algorithm.

    3. Optionally, if the element is block-level, the outline of the element (see 10 below).

  8. All positioned, opacity or transform descendants, in tree order that fall into the following categories:

    1. All positioned descendants with 'z-index: auto' or 'z-index: 0', in tree order. For those with 'z-index: auto', treat the element as if it created a new stacking context, but any positioned descendants and descendants which actually create a new stacking context should be considered part of the parent stacking context, not this new one. For those with 'z-index: 0' treat the stacking context generated atomically.

    2. All opacity descendants with opacity less than 1, in tree order, create a stacking context generated atomically.

    3. All transform descendants with transform other than none, in tree order, create a stacking context generated atomically.
  9. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order (smallest first) then tree order.

Now seriously, refer to the w3c paint order documentation

In point 4.1, the background of children is painted

In point 4.4, the border of children is painted.

When point 4 is finished, all background and border of your snippet have been painted

Now, in point 7.2.1.5.1.1.3, the text of the children is painted.

This is the behaviour that you are seeing.

Notice also that is easy to change this behaviour. We can activate point 8.2, (setting opacity) and it will paint like you may have expected:

body {    margin: 0;    background: pink;    color: #fff;  }    .box {    margin-top: 20px;    background: red;  }    .bottom {    text-align: right;    background: green;    animation: animate 2s infinite alternate linear;    opacity: 0.9999;  }    @keyframes animate {    from {      margin-top: 10px;    }    to {      margin-top: -40px;    }  }
<div class="box">    some content  </div>  <div class="bottom">    other content  </div>

Another snippet, showing several point in the document:

Notice that all the border and background in step 4 are rendered after step 3, and before setp 5. But the text in step 4 is step 7, so is rendered after text in step 5

div {    width: 200px;    height: 100px;    border: solid 10px;    font-size: 40px;  }    .step3 {    border-color: red;    background-color: lightpink;    z-index: -1;    position: relative;    margin-left: 10px;  }    .step41 {    border-color: brown;    background-color: yellow;    margin-left: 30px;    margin-top: -40px;  }    .step42 {    border-color: blue;    background-color: lightblue;    margin-left: 50px;    margin-top: -40px;    color: red;  }    .step43 {    border-color: green;    background-color: lightgreen;    margin-left: 160px;    margin-top: -150px;    color: crimson;  }    .step5 {    float: left;    background-color: white;    margin-top: -30px;  }    div:hover {    position: relative;  }
<div class="step3">Step 3 negative zindex</div>  <div class="step41">step4 In flow, number 1</div>  <div class="step42">In flow, number 2</div>  <div class="step43">In flow, number 3</div>  <div class="step5">step 5 float</div>

I don't know if this counts as an use case : this more natural the initial behaviour the the one set by the elements positioning relative

div {    width: 100px;    height: 1.3em;    border: solid 12px tomato;    font-size: 18px;  }    div:hover {    position: relative;  }
<div>a long stretch of text overflowing to the other div</div>  <div></div>
like image 67
vals Avatar answered Oct 13 '22 22:10

vals


The answer to the question is nowhere near everyone is digging. Because we all have fixed our mind at believing that this is something that leverages user experience anyhow, something intuitive. Have anyone thought this can be for any CPU processing benifit?

Browser rendering engine does not paint an element completely, and then goes on painting the next, because we have GPU technology. The rendering engine paints elements in different stacked layers one over another which are handed over to the GPU who then rasterizes all the layers into a composite layer that shows up on screen.

Browser rendered layers

So, how the layers are created?

  • Rendering engine paints all backgrounds and borders on the base layer.
  • Creates a new layer for an element who has positioning specified. The layer is stacked over the base layer for the GPU to rasterize it. When the element moves the rendering engine only tells the GPU to move the layer, GPU does the rest. CPU is saved from painting overhead.
  • Creates a new layer for an element who has opacity specified. When opacity changes GPU does the rasterizing work.
  • Similarly creates a new layer for inputs, buttons, canvases, videos everything that can cause CPU to paint repeatedly, is transfered to the GPU as a new layer. We are saving a lot CPU processing, right? :)
  • Denies creating any new layer for an element with negative margin :( . This is obvious browser cannot issue new layer for every element with margin, there are a lot of them. Even if it has negative margin it may or may not overlap other element. The only way to be sure is to look into the render tree. What's the point? We are trying to save processing, looking into render tree for every element will ruin it.
  • Issues a new layer for text element. GPU can rasterize text far more efficiently onto a background, so why paint it with CPU on the base layer?? This was the original question. I think everyone can answer it now.

Why text is painted on a separate layer above base layer?

  • CPU does not need to paint the text over background, CPU is happy.
  • When text color or shadows change CPU only paints the text layer, GPU rasterizes it on the background.
  • When the background changes CPU only paints the background layer, GPU rasterizes text over it.
  • When text is scrolled over a fixed background, no painting is needed, CPU only tells GPU to move the text layer, GPU does the rest.
  • And a lot more...

Now let's look into the magics we are encountering due to this.

painting and rasterizing

  • Case 1: When position relative is added to 2nd div it gets a new layer, this new layer is stacked over the base layer and even over the text layer of first div.
  • Case 2: Same thing happens when opacity is applied.
  • Case 3: Any transform style will create a new layer.
  • Case 4: This one is interesting. The margin does not create any layer for the second div, it is painted on the base div after the first div is painted, so it is painted over the first div on the same layer. The text on the first div is on it's own layer that is stacked over the base layer so GPU rasterizes the text over the background of second div.

[source: developers.google.com]

like image 37
Munim Munna Avatar answered Oct 13 '22 22:10

Munim Munna