1

I was trying to create a container with a pseudo-element (::before, in this case) where on the parent element I would have a linear-gradient background with some transparency and on the pseudo-element, I would have the color.

The main reason to that here is that I also wanted to have some backdrop-filter applied to it and by doing in this way, I could apply an opacity to the pseudo-element only (in this specific case, I couldn't use a background color with some alpha, so that's why the whole workaround here).

I also wanted to only apply the opacity when the browser actually supported backdrop-filter, so these styles are contained within a @supports block.

So, here is when things get weird. This element is position: fixed, and I noticed that for the browsers not supporting backdrop-filter, the linear-gradient was not being applied.

Here's an example of what I mean (https://jsfiddle.net/8aeczvf2/). On this example I have some divs with the same style:

.container {
  position: relative;
  top: 0;
  height: 100px;
  width: 200px;
  background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.5));
}

.container::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background-image: url('https://lorempixel.com/600/400/');
  z-index: -1;
}

When .container has position: relative, it works as expected. But, if I change it to position: fixed, then the pseudo-elements background seems to be applied on top of the parent's. Same goes if I only apply az-indexto.container. It works again on the last example, but because::before` has some opacity applied to it.

Is this expected by the browser to render the pseudo-elements differently depending on the type of position applied to the parent?

Diego Cardoso
  • 973
  • 4
  • 14

1 Answers1

2

Yes this is the expected behavior. Let's start explaining them one by one:

1) When the parent is position:fixed

In this case, the parent will create a stacking context forcing all its descedants to be painted inside. Whataver the z-index you specify to the pseudo element (negative or positive), it will always be on the top of its parent.

2) When the parent is position:relative

In this case, the parent doesn't create a stacking context making the pseudo element a part of an upper stacking context. This means that both parent and pseudo element belong to the same stacking context and the z-index will define which one should be on the top. Since you are defined a negative one for the pseudo element it will logically be below.

3) When the parent is position:relative with a z-index specified

In this case, the parent will create a stacking context like in (1) because a value different from auto for z-index will do so and the pseudo element will be trapped inside.

More details here: Why can't an element with a z-index value cover its child?


Now let's talk about the margin that make the first case slightly different. If you inspect all the 3 different codes you will see that the default margin of p is rendred outside and this is due to collapsing margins but not in the first case when using position:fixed because:

Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with 'overflow' other than 'visible' (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents. ref

And when an element establish a BFC it disable the margin collapsing with its child element that's why the margin of p is rendred inside in first example which explain the offset of the text.

If you disable the margin collapsing in the other cases you will have the same offset:

.container {
  position: relative;
  overflow:hidden;
  top: 0;
  height: 30px;
  width: 200px;
  background-image: linear-gradient(rgba(0,0,0,0.2),rgba(0,0,0,0.2));
}

.container::before {
  content: "";
  position: absolute;
  width: 100%;
  height: 100%;
  background: red;
  z-index: -1;
}

.fixed {
  position: fixed;
}

.margin-top {
  margin-top: 50px;
}

.z-index {
  z-index: 1;
}
<div class="container fixed">
  <p>
    container fixed
  </p>
</div>

<div class="container margin-top">
  <p>
    container relative
  </p>
</div>

<div class="container z-index">
  <p>
    container relative with z-index
  </p>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • I was more concerned about the different behavior on the relationship between element and its pseudo-elements children, as you covered quite well on the answer. The paragraph was just part of the example to show each case, but thanks to that explanation as well. Much appreciated. – Diego Cardoso Sep 06 '19 at 10:09