Z 스택

Z-stack

여러 요소를 같은 그리드 칸(`grid-area`)에 겹쳐 넣어, `position: absolute` 없이 레이어를 쌓는 방법.

다른 이름: Grid stacking contextOverlap via grid-area
···
html
<div class="wrap">
  <div class="zstack" id="zstack">
    <div class="layer l1">Base</div>
    <div class="layer l2">Badge</div>
    <div class="layer l3"></div>
  </div>
  <div class="tag" id="tag">front: Base</div>
</div>
css
.wrap{display:flex;flex-direction:column;align-items:center;gap:10px}
.zstack{display:grid;width:min(42vmin,190px);aspect-ratio:1;place-items:center}
.layer{grid-area:1/1;border-radius:50%;display:grid;place-items:center;font-weight:800;
  transition:transform .5s cubic-bezier(.34,1.56,.64,1),opacity .5s}
.l1{width:100%;height:100%;background:var(--surface);border:2px solid var(--line);color:var(--muted);font-size:11px}
.l2{width:52%;height:52%;background:var(--accent);color:#fff;font-size:11px}
.l3{width:80%;height:80%;border:3px dashed var(--accent-2);background:transparent}
.layer.front{transform:scale(1.12)}
.layer.back{opacity:.5}
js
const l1 = document.querySelector('.l1');
const l2 = document.querySelector('.l2');
const l3 = document.querySelector('.l3');
const tag = document.getElementById('tag');
const order = [
  [l1, 'Base'],
  [l3, 'Ring'],
  [l2, 'Badge'],
];
let i = 0;
function apply() {
  [l1, l2, l3].forEach((l) => l.classList.add('back'));
  const [front, name] = order[i % order.length];
  front.classList.remove('back');
  front.classList.add('front');
  setTimeout(() => front.classList.remove('front'), 480);
  tag.textContent = 'front: ' + name;
  i++;
}
apply();
setInterval(apply, 1500);

여러 요소를 겹치는 전통적인 방법은 부모에 `position: relative`, 자식마다 `position: absolute; inset: 0`이었습니다. 이 방식은 자식이 부모의 정상적인 문서 흐름에서 완전히 빠져나오기 때문에, 크기를 percentage로 잡으려면 부모 높이를 따로 지정해야 하고 flex·grid 같은 정렬 시스템과도 잘 어울리지 않습니다.

CSS Grid에서는 더 간단한 방법이 있습니다: 부모에 `display: grid`만 주고, 겹치고 싶은 자식마다 똑같은 `grid-area`(또는 똑같은 `grid-row`/`grid-column`) 값을 줍니다. 명시적으로 이름 붙인 영역이 없으면 암묵적으로 첫 번째 칸(`1 / 1`)이 만들어지고, 그 칸에 배정된 자식들은 전부 같은 위치에서 서로 겹칩니다. `position`을 전혀 건드리지 않고도 겹쳐지므로, 각 레이어는 `place-self`·`justify-self` 같은 그리드 정렬 속성을 그대로 쓸 수 있습니다.

쌓이는 순서(어느 게 위로 오는지)는 DOM 순서를 따르며, 필요하면 `z-index`로 뒤집을 수 있습니다. 뱃지가 이미지 위에 얹히는 카드, 아이콘 위에 진행률 링이 도는 위젯처럼 "레이어 몇 개가 정확히 같은 자리에서 정렬돼야 하는" 상황에 이 패턴이 `position: absolute`보다 코드가 적고 반응형에도 더 안전합니다.

언제 쓰나

배지·아이콘·오버레이처럼 몇 개의 레이어가 정확히 같은 위치에서 정렬돼야 할 때 씁니다. 레이어가 서로 다른 위치로 자유롭게 흩어져야 하면 여전히 `position: absolute`가 맞습니다.