
z-index, stacking context를 정확히 이해하기 위해서는 기본적으로 브라우저가 어떤 기준으로 요소들을 쌓아 시각적으로 먼저 노출하는지 알아야 한다.
브라우저는 기본적으로 html구조 순서대로 element들을 쌓아 시각적으로 어떤 것이 먼저 보일 지 쌓임의 순서를 결정한다.
하지만, 기본적인 쌓임 순서를 두 가지 속성을 이용하여 순서를 조작할 수 있다.
z-indexposition
<body>
<div class="overlap">
<div class="box1">1</div>
<div class="box2">2</div>
<div class="box3">3</div>
</div>
</body>
body {
width: 100vw;
height: 100vh;
}
.overlap {
display: grid;
grid: 1 / 1;
width: 400px;
}
.overlap div {
grid-row-start: 1;
grid-column-start: 1;
width: 200px;
height: 200px;
}
.box1 {
background-color: red;
transform: translateX(50px);
}
.box2 {
background-color: orange;
transform: translateX(100px);
}
.box3 {
background-color: green;
transform: translateX(150px);
}

3개의 박스의 높이와 넓이가 200px이지만 브라우저의 기본 쌓임 순서대로 쌓여 맨 마지막에 선언한 div.box3가 가장 상위로 노출된다.
브라우저에서 position을 적용하여 쌓임순서를 조작할 수 있다.
하지만, 어떤 기준을 근거로 쌓이는지를 알아야 하는데 그 기준이 아래 그림과 같다.

상위에 노출 시키고자 무작정 position: relative를 사용했지만, 다른 stacking context라면 다르게 동작한다.
z-index는 요소가 overlap(겹침)됐을 때, z-axis(z축)를 기준으로 z-order를 결정하여 Screen상에 시각적으로 어떤 요소를 먼저 노출시킬시 결정하는 css 속성이다. 이 때 중요한 것은 z-index는 stacking context내에서 결정되고 유효하며 stacking context내의 요소들은 벗어날 수 없는 경계가 존재한다.
좀 더 쉽게 풀어서 설명하자면, 하위 stacking context내에 있는 요소의 z-index가 아무리 크더라도 z-index가 더 작은 상위 stacking context에 있는 요소가 시각적으로 먼저 노출된다.(아래 그림 참고)

💡 z-index는 positioned element(position: static ❌) 요소에만 동작한다.
<z-index와 position 쌓임 순서>

그럼 stacking context가 어떤 것이고 언제 생성이 되는지 알아보자
stacking context는 공통 부모요소를 가지는 자식 요소들의 그룹이다.
위에서 언급했 듯이 동일한 stacking context내에서 z-index가 특정되며 유효하다고 했다.
결국 z-index의 동작원리를 이해하는 가장 키 포인트는 같은 stacking context를 공유하는지에 대한 유무에 있다. 따라서 Stacking context가 어느 상황에 생성되는지를 알아야 한다.
stacking context가 생성되는 경우는 매우 많지만 자주 쓰는 것으로 추려보면 아래와 같다.
z-index의 auto가 아님과 동시 에 relative, absolute이제 다시 위의 overlap의 코드를 보면 transform이 적용되었으므로 각각의 box들은 각기 다른 stacking context에 생성된다.

이제 z-index는 stacking context에 제약된다는 사실을 증명하기 위해 모든 box의 자식으로 span을 추가하고 box1의 자식에 큰 z-index를 주자.
<body>
<div class="overlap">
<div class="box1">
<span>1</span>
</div>
<div class="box2">
<span>2</span>
</div>
<div class="box3">
<span>3</span>
</div>
</div>
</body>
.box1 > span {
width: 100px;
height: 100px;
background-color: white;
z-index: 100;
}

z-index를 100이나 주었는데 box3보다 아래에 깔려있다. 이렇게 요소가 겹치게 되면 무작정 z-index를 준다고 상위로 노출되지 않는구나 확인할 수 있다.
이제 box1>span을 노출 시키기 위해서 방법을 찾아보자. 일단, 내가 생각한 것은 2가지 방법이 존재한다.
상황에 따라 다른 전략을 선택하면 되겠지만 난 2번을 선택하여 span만 노출시키면 되는 방향으로 가보겠다.
.box1 {
...//
z-index: 1;
}

<위 코드 내 요소들의 쌓임 순서 시각화>

이렇게 stacking context 자체의 z-order를 조절하여 문제를 해결할 수 있다.
하지만, 이런 상황에서 box1>span은 box1이 생성한 stacking context에 있기 때문에 box1자체도 상위로 노출됨은 필연적이다.
만약에 box1말고 span만 노출시키고 싶다면 1번의 전략을 선택하면 해결할 수 있다.
stack overflow에서 같은 문제에 대한 해결책으로도 z-index가 원하는 대로 동작하기 위해서는 같은 stacking context를 공유해야한다고 한다.
이것저것 실험해보면서 추가로 정리한 것을 정리해보자.
공통 부모요소를 가지는 자식 요소들의 그룹이라고 어렵게 생각하면 초점이 자식들의 그룹으로 생각하여 요소들의 z-order를 추적하기 어렵지만 stacking context를 촉발시키는 그 요소 자체(공통 부모)를 다른 layer 즉, stacking context라 생각하면 z-order를 추적하기 쉽다.z-index 또한 줄 수 있다.input type range가 가지고 있는 기본적인 브라우저 스타일을 제거하면서 dual range slider를 구현하다가 z-index가 원하는대로 작동하지 않아 찾다보니 stacking context의 존재를 알고 문제를 해결할 수 있었다.
또한 flex나 grid에서 z-index를 주고 적용되는 것에 아무런 의문을 품지 않았다.
flex나 grid가 적용된 자식들은 사실상 position: static이므로 z-index를 준다고 적용되는것이 아니기 때문이다. 하지만, mdn에서 정의한 위의 조건들을 기반으로 하면 flex container내의 자식 요소에게 z-index를 주면 stacking context가 생성되어 z-order를 적용할 수 있다는 것을 알았다.
position
when does the stacking context created by mdn
z-index & stacking context explanation blog