z-index가 하는 일
요소가 겹치면 z-index가 어느 것을 앞에 그릴지 정해요. 숫자가 높은 쪽이 이겨요. 위치 지정 요소(static이 아닌 모든 position)와 flex, grid 항목에서 동작해요.
z-index가 없으면 HTML에서 나중에 나오는 요소가 앞의 요소 위에 그려져서 C가 B를, B가 A를 덮어요. z-index 값이 이를 뒤집어요. A가 맨 앞, 그다음 C, 그다음 B예요.
값
| 값 | 뜻 |
|---|---|
auto (기본값) | 형제들과 함께 레벨 0에 쌓이고, 쌓임 맥락을 만들지 않아요 |
0 | 레벨 0이지만 쌓임 맥락을 만들어요 |
| 양의 정수 | 더 낮은 값 앞에 놓여요 |
| 음의 정수 | 레벨 0 뒤에, 부모가 쌓임 맥락을 만든다면 부모 콘텐츠 뒤에 놓여요 |
정수만 유효해요. z-index: 1.5는 무시돼요.
z-index 9999가 질 수 있는 이유: 쌓임 맥락
z-index 값은 페이지 전체에서 비교되지 않아요. 요소들은 쌓임 맥락으로 묶이고, z-index는 같은 맥락 안의 요소들끼리만 순서를 정해요. 맥락 자체는 그 맥락을 만든 요소의 z-index로 하나의 레이어로 배치돼요.
아래에서 파란 드롭다운은 z-index: 9999지만 z-index: 1인 헤더 안에 있어요. 그 헤더의 형제인 초록 배너는 z-index: 2예요. 드롭다운을 포함한 헤더 레이어 전체가 배너 아래에 있어요.
버튼을 눌러 보세요. z-index: auto가 되면 헤더가 더 이상 쌓임 맥락을 만들지 않아서, 드롭다운의 9999가 배너의 2와 직접 비교되고 드롭다운이 앞으로 나와요. 드롭다운을 99999로 올렸어도 아무 소용이 없었을 거예요.
쌓임 맥락을 만드는 것
다음 중 하나라도 해당하면 요소가 자기만의 쌓임 맥락이 되어 안에 있는 모든 것의 z-index를 가둬요.
| 속성 | 조건 |
|---|---|
position: relative 또는 absolute | auto가 아닌 z-index와 함께 |
position: fixed 또는 sticky | 항상 |
| flex나 grid 항목 | auto가 아닌 z-index와 함께 |
opacity | 1 미만 |
transform, scale, rotate, translate | none이 아닌 모든 값 |
filter, backdrop-filter | none이 아닌 모든 값 |
isolation | isolate |
mix-blend-mode | normal이 아닌 모든 값 |
will-change | 위 속성 중 하나를 지정할 때 |
contain | layout이나 paint(그리고 strict, content) |
뜻밖인 것은 opacity와 transform이에요. 부모에 페이드나 hover 애니메이션을 추가했더니 갑자기 드롭다운이 다음 섹션 뒤로 들어가는 일이 생겨요.
툴팁의 z-index: 10이면 다음 열 위에 놓여야 해요. 그런데 opacity: 0.99가 첫 번째 열을 z-index auto(레벨 0)인 쌓임 맥락으로 만들고, 두 번째 열은 같은 레벨에서 HTML상 나중에 나오기 때문에 툴팁을 포함한 첫 번째 열 전체 위에 그려져요. opacity 줄을 지우면 툴팁이 다시 앞으로 나와요.
음수 z-index
음수 z-index는 요소를 형제들 뒤로 보내요. 부모가 쌓임 맥락을 만들지 않으면 부모의 배경 뒤로까지 갈 수 있어서 자주 당황하게 돼요. 부모에 isolation: isolate를 주면 다른 시각적 효과 없이 맥락을 만들어서 음수 자식을 그 안에 붙잡아 둬요.
isolation이 없으면 주황색 틀이 버튼 자체의 회색 배경 뒤로 떨어져서 가장자리만 보여요. isolation: isolate가 있으면 틀이 버튼의 쌓임 맥락 안에 머물러요. 배경 위, 텍스트 아래예요.
z-index 단계
숫자를 계속 올리지 말고 사이트 전체에 쓸 이름 붙은 레이어 몇 개를 정하세요.
:root {
--z-dropdown: 10;
--z-sticky: 20;
--z-overlay: 30;
--z-modal: 40;
--z-toast: 50;
}
.modal { position: fixed; z-index: var(--z-modal); }
대부분의 레이어 버그는 숫자 버그가 아니라 쌓임 맥락 버그예요. 그래서 작은 단계와, 안에서 z-index를 쓰는 컴포넌트에 isolation: isolate를 주는 것만으로 많이 해결돼요. z-index가 동작하려면 위치 지정이 필요하고, 이는 position 페이지에서 다뤄요.
자주 하는 실수
- static 요소에 z-index.
position: relative를 추가하세요(또는 flex나 grid 항목으로 만드세요). - 9999까지 올리기. 부모가 경쟁 요소보다 아래에 있는 쌓임 맥락을 만들면 어떤 자식 값도 이길 수 없어요. 대신 그 맥락을 찾으세요.
- 부모에 새로 넣은
opacity,transform,filter. 갑자기 드롭다운을 가둘 수 있어요. - 소수 값.
z-index: 1.5는 잘못된 값이에요. z-index: 0과auto가 같게 동작할 거라고 기대하기.0은 쌓임 맥락을 만들고,auto는 만들지 않아요.- 깊이 중첩된 컴포넌트 안의 모달.
<body>끝 가까이에 렌더링하거나(또는<dialog>요소의 top layer를 쓰거나) 해서 어떤 조상 맥락도 가두지 못하게 하세요.
자주 묻는 질문
z-index는 무엇을 하나요?
겹친 요소의 쌓임 순서를 정해요. 같은 쌓임 맥락 안에서 값이 높은 쪽이 낮은 쪽 앞에 그려져요. 기본값은 auto이고, 값은 음수를 포함한 정수예요.
z-index가 동작하지 않는 이유는 무엇인가요?
요소에 위치가 지정되지 않았거나(z-index는 static이 아닌 position, 또는 flex나 grid 자식이어야 해요), 순서가 더 낮은 쌓임 맥락을 만드는 부모 안에 있기 때문이에요. 자식은 자기 z-index와 상관없이, 자기 쌓임 맥락보다 위에 놓인 요소를 넘어설 수 없어요.
쌓임 맥락(stacking context)이란 무엇인가요?
하나의 레이어로 함께 쌓이는 요소 묶음이에요. 안에서는 자식들이 z-index로 정렬되고, 밖에서는 그 맥락을 만든 요소의 z-index로 묶음 전체가 배치돼요.
무엇이 쌓임 맥락을 만드나요?
여러 가지가 있어요. 루트 요소, auto가 아닌 z-index를 가진 위치 지정 요소, position: fixed나 sticky, z-index가 있는 flex나 grid 자식, 1 미만의 opacity, transform, filter, isolation: isolate, 그리고 이 속성들에 대한 will-change예요.
z-index는 position 없이도 동작하나요?
일반 block이나 inline 요소에서는 안 돼요. position: static은 무시해요. flex 항목과 grid 항목에서는 position 없이도 동작해요.