blog
opacity가 z-index를 망가뜨리는 이유: Stacking Context
Front-end / CSS#CSS#z-index#stacking context#opacity
opacity를 준 요소 안에서 z-index: 999를 줬는데도 다른 요소 아래에 깔리는 경우가 있다. opacity가 z-index를 무효화하는 것이 아니라, 새로운 stacking context를 만들기 때문이다.
stacking context란
z-index는 같은 stacking context 안의 형제끼리만 비교된다. 어떤 요소가 stacking context를 만들면, 그 자식들의 z-index는 아무리 커도 부모의 쌓임 순서 안에서만 유효하다.
stacking context를 만드는 대표적인 조건:
opacity가 1 미만position이relative/absolute이면서z-index가auto가 아님position: fixed또는stickytransform,filter,perspective,will-change(해당 속성),isolation: isolate,mix-blend-mode등
예제
<div style="opacity: 0.5; background: red; width: 100px; height: 100px;">
Box 1
<div style="position: absolute; z-index: 999; background: green; width: 50px; height: 50px;">
Inner Box
</div>
</div>
<div style="position: relative; z-index: 1; background: blue; width: 100px; height: 100px; margin-top: -50px;">
Box 2
</div>
- Box 1은
z-index가 없지만opacity: 0.5때문에 stacking context가 된다. - Inner Box의
z-index: 999는 Box 1 내부에서만 비교된다. 바깥에서 보면 Inner Box는 Box 1 덩어리의 일부일 뿐이다. - Box 1 덩어리는
z-index: 1인 Box 2보다 아래에 쌓이므로, Inner Box도 Box 2 아래에 그려진다.
Box 1에서 opacity를 빼면 stacking context가 사라지고, Inner Box의 z-index: 999가 Box 2와 같은 레벨에서 비교되어 위로 올라온다.
해결 방법
- 배경만 투명하게: 요소 전체가 아니라 배경만 반투명이면 되는 경우가 많다.
background-color: rgb(255 0 0 / 0.5)는 stacking context를 만들지 않는다. - DOM 위치 옮기기: 모달·드롭다운·툴팁처럼 위로 떠야 하는 요소는 stacking context 밖(보통
body바로 아래)으로 렌더링한다. React의createPortal, Vue의<Teleport>가 이 용도다. - 쌓임 순서 재설계: 부모 stacking context 자체의
z-index를 조정한다.
참고로 자식에 position: fixed를 줘도 부모 stacking context를 벗어나지 못한다. 오히려 부모에 transform·filter가 있으면 fixed 요소의 기준점까지 부모로 바뀐다.
요약
opacity < 1은 새 stacking context를 만든다.- 자식의
z-index는 그 context 안에서만 의미가 있다. - 배경 투명도는 색상 알파값으로, 떠야 하는 UI는 Portal/Teleport로 해결한다.