z-index, stacking context를 정확히 이해하기 위해서는 기본적으로 브라우저가 어떤 기준으로 요소들을 쌓아 시각적으로 먼저 노출하는지 알아야 한다.
브라우저는 기본적으로 html구조 순서대로 element들을 쌓아
시각적으로 어떤 것이 먼저 보일 지 쌓임의 순서를 결정한다.
하지만, 기본적인 쌓임 순서를 두 가지 속성을 이용하여 순서를 조작할 수 있다.
z-index
position
<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