Why does position:relative; appear to change the z-index?

Solution 1:

You need to refer to the specification and more precisely the painting order to understand when each layer is painted.

Without position:relative your element is not positioned and will be painted at the step (4):

  1. 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:

Then we paint the positioned elements (including the .mask) at the step (8)

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

Now when you add position:relative you make the container also positioned thus it will fall in the step (8) too and as described there we consider the tree order since both don't have any z-index specified. So the .container will painted later in this case.

If you change the order of the element (you make the container before the mask) you will notice that position:relative won't have any effect because in both cases the painting order will be the same:

body {
  margin: 0;
  font-family: arial;
}
section {
  position: relative;
  background: url(https://preview.webpixels.io/boomerang-v3.6.1/assets/images/backgrounds/slider/img-41.jpg)
    no-repeat left center/cover;
  height: 70vh;
  display: flex;
  justify-content: center;
}
.container {
  position: relative; /* you can remove this*/
  width: 100%;
  max-width: 1280px;
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
}
.mask {
  position: absolute;
  width: 100%;
  height: 100%;
  background: #3452ff;
  opacity: 0.7;
}
<section>
  <div class="container">
    <h1>Hello World</h1>
  </div>
  <div class="mask"></div>
</section>

If we check the step (8) it also said opacity or transform which means that if you also change the opacity of the container or add a transform, the order will change too.

body {
  margin: 0;
  font-family: arial;
}
section {
  position: relative;
  background: url(https://preview.webpixels.io/boomerang-v3.6.1/assets/images/backgrounds/slider/img-41.jpg)
    no-repeat left center/cover;
  height: 70vh;
  display: flex;
  justify-content: center;
}
.container {
  transform:translate(0); /*added this*/
  width: 100%;
  max-width: 1280px;
  display: flex;
  justify-content: center;
  align-items: center;
  color: white;
}
.mask {
  position: absolute;
  width: 100%;
  height: 100%;
  background: #3452ff;
  opacity: 0.7;
}
<section>
  <div class="mask"></div>
  <div class="container">
    <h1>Hello World</h1>
  </div>
</section>

It's also trivial to notice that if you add z-index (either negative or positive) you will also affect the painting order and in this case the tree order will have no effect.

  1. Stacking contexts formed by positioned descendants with negative z-indices (excluding 0) in z-index order (most negative first) then tree order

....

  1. Stacking contexts formed by positioned descendants with z-indices greater than or equal to 1 in z-index order (smallest first) then tree order.

We paint the element with negative z-index at (3) and the positive ones at (9) and between those steps we have all the cases where z-index is not involved like described initially.