C.W.K.
Stream
Lesson 03 of 04 · published

position, z-index, 그리고 stacking context

~12 min · position, absolute, fixed, sticky, z-index, stacking-context

Level 0Markup Novice
0 XP0/34 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"position 은 normal flow 의 비상구. z-index 는 탈출한 다음 어떻게 stack 되는지. 대부분의 layout 버그가 하나 또는 둘 다 오해에서 살아."

다섯 position

  • static — 기본. Element 가 normal flow 에 앉음. top/right/bottom/left 효과 없음. z-index 효과 없음.
  • relative — normal flow 에 있지만, 자연 위치에서 top/right/bottom/left 만큼 시각적으로 offset. Element 의 예약 공간은 여전히 원래 자리. 또: absolute 자식의 positioning context 가 됨.
  • absolute — normal flow 에서 제거. 가장 가까운 positioned ancestor (static 아닌 position 가진 어떤 ancestor) 기준 위치. Positioned ancestor 없으면 초기 containing block (문서는 viewport, iframe 은 iframe) 기준.
  • fixed — normal flow 에서 제거. Viewport 기준 위치. 페이지 스크롤 시 안 움직임.
  • sticky — 스크롤 임계점 도달까지 normal flow 에 있다가, 그 다음 containing block 안에서 fixed 처럼 행동. Sticky header, 행 스크롤 동안 보이는 table header 에.

Containing Block 질문

absolute positioning 에서, "뭘 기준으로 positioned?" 질문이 position 자체보다 중요. 답:

  • position: relative, absolute, fixed, sticky, 또는 transform/filter/perspective 설정된 가장 가까운 ancestor.
  • 그런 ancestor 없으면 초기 containing block (viewport).

Canonical 패턴: 부모가 offset 없이 position: relative, 자식이 offset 가진 position: absolute. Relative 부모가 positioning context; absolute 자식이 안에서 position.

Sticky 는 추가 주름 있음

position: sticky직접 부모의 박스 안에 붙어. 전역적으로 viewport 에 안 붙음 — 부모가 스크롤로 지나가면, sticky element 도 함께 스크롤되어 사라짐. position: sticky 가 table header 에 작동하는 이유 (table 스크롤 동안 붙음); 하지만 flex/grid 자식 컨테이너 안의 position: sticky 가 종종 "작동 안 함" — 부모의 height 가 스크롤할 공간 안 허용해서.

Sticky 가 필요한 두 가지:

  1. 스크롤 방향 정의 (top: 0, bottom: 0, left: 0 등).
  2. 부모의 height 가 sticky element 의 예약 height 보다 큼 (아니면 붙을 자리 없음).

z-index 와 stacking context

Element 가 overlap 될 때 (positioned 여서), z-index 가 어느 게 위에 갈지 결정. 높은 숫자 이김. 하지만 — 그게 함정 — z-index같은 stacking context 안 element 만 비교.

새 stacking context 가 만들어지는 방법:

  • position: relative/absolute/fixed/sticky WITH auto 아닌 z-index
  • position: fixed 또는 sticky z-index 무관
  • opacity 1 미만
  • transform, filter, backdrop-filter
  • will-change 가 stacking context 만드는 property 로 설정
  • isolation: isolate — 모던하고 의도적 방법
  • contain: layout, paint, 또는 strict
  • z-index 설정된 Flexbox/grid 항목

클래식 혼란: modal 의 z-index: 999999 가 stacking context 설정한 부모 안에 갇혀 있으면 무관한 element 의 z-index: 10 위에 안 올라감. 그 부모 안에서 modal 은 맞게 stack; 바깥에선 부모의 stack 레벨이 중요한 거.

의도적으로 stacking context 만들 때 isolation: isolate 써. "이 영역은 자기 z-index 세계" 라고 말하는 모던하고 부작용 없는 방법. Sibling 으로 z-index 새지 않게 하고 싶은 component 의 root, 또는 부모 z-index 오염에서 보호하고 싶은 영역에 써.

멀쩡한 z-index Layer 디자인

무작위 숫자 (z-index: 9999, z-index: 999999) 고르지 마. 작은 layer set 을 custom property 로 정의하고 이름으로 써:

모던 가운데 정렬 패턴

가운데 정렬이 옛날엔 정렬하는 게 뭔지 따라 패턴 셋 필요 (margin auto, line-height, table-cell, 음수 margin 가진 absolute positioning, transform translate). 모던 CSS: 부모에 display: grid; place-items: center; 가 어떤 자식이든 두 축에 가운데. display: flex; align-items: center; justify-content: center; 도 같은 거.

피파의 노트

Cwk-site 의 modal 레이어가 modal portal root 에 isolation: isolate 써, 신경 쓰는 몇 layer 의 z-index 변수와 함께: --z-dropdown, --z-modal-backdrop, --z-modal, --z-toast. 변수가 layering 을 프로젝트 레벨에서 보이게; isolation 이 어떤 component 의 z-index 든 우연히 전역 stack 과 싸우는 거 방지. 마법 숫자 그만 고르고 layer 이름 짓기 시작하면 클래식 z-index 전쟁이 사라져.

Code

각 position 값 실제·css
/* position: relative — 공간 유지, 시각적 offset */
.relative-demo {
  position: relative;
  top: 20px;             /* 자연 위치에서 아래로 20px offset */
  left: 10px;            /* 오른쪽으로 10px offset */
  /* 원래 'slot' 은 여전히 flow 에 예약 — sibling 이 element 가 여전히
     자연 위치에 있는 것처럼 행동 */
}

/* position: absolute — flow 밖, 가장 가까운 positioned ancestor 기준 */
.parent {
  position: relative;    /* positioning context 설정 */
  width: 300px;
  height: 200px;
}
.parent .badge {
  position: absolute;
  top: 8px;
  right: 8px;            /* .parent 의 top-right 에서 8px */
}

/* position: fixed — viewport 기준, 페이지 스크롤에 면역 */
.fab {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;         /* floating action 버튼, 항상 보임 */
}

/* position: sticky — 스크롤 임계점까지 flow, 그 다음 붙음 */
thead {
  position: sticky;
  top: 0;                /* table 스크롤 시 viewport top 에 붙음 */
  background: white;
  z-index: 1;
}
isolation: isolate + 명명된 z-index layer·css
/* Stacking context — isolation: isolate 로 명시적으로 만듦 */

.modal-portal {
  isolation: isolate;     /* 이 영역은 자기 z-index 세계 */
}
.modal-portal .backdrop { z-index: var(--z-modal-backdrop); }
.modal-portal .modal { z-index: var(--z-modal); }

/* z-index layer 를 변수로 — 예측 가능, 감사된 */
:root {
  --z-base: 0;
  --z-dropdown: 100;
  --z-sticky-header: 200;
  --z-overlay: 300;
  --z-modal-backdrop: 400;
  --z-modal: 500;
  --z-toast: 600;
  --z-tooltip: 700;
}

/* 이제: */
.menu { z-index: var(--z-dropdown); }
.toast-region { z-index: var(--z-toast); }

/* Layer 리팩토링이 변수 하나 바꾸기지 47 개 마법 숫자가 아니야 */
가운데 정렬 — 세 접근법·css
/* 가운데 정렬, 모던 방법 */

/* Grid (가장 간결) */
.center-with-grid {
  display: grid;
  place-items: center;   /* align-items + justify-items 의 약자 */
  min-height: 100vh;     /* viewport 차지 */
}

/* Flexbox (명시적) */
.center-with-flex {
  display: flex;
  align-items: center;    /* 수직 */
  justify-content: center; /* 수평 */
  min-height: 100vh;
}

/* Absolute positioning (옛 답) */
.center-with-abs {
  position: relative;
}
.center-with-abs .child {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  /* 작동하지만 verbose, 부모가 positioned 여야 하는 거 알아야 하고,
     translate 가 stacking-context-creator. Grid/flex 선호. */
}

External links

Exercise

Card 의 position: relative + badge 의 position: absolute 써서 카드 top-right 코너에 알림 배지 가진 카드 만들기. 그러고 나서 긴 리스트에 sticky header 추가. <body> 바닥에 isolation: isolate 가진 modal portal 만들고, (DevTools 의 Layers 패널에서) modal 의 stacking context 가 페이지 다른 어떤 것과도 독립인지 확인. 마지막으로 z-index layer 변수 다섯 정의하고 CSS 의 무작위 z-index 를 그걸 쓰게 리팩토링.
Hint
DevTools → Rendering (또는 Layers) 탭이 stacking context 보여 줘. z-index 가 기대대로 작동 안 하면 stacking context 만든 ancestor 찾아 — opacity < 1 과 transform 이 보통 범인.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.