레이어
앱 크롬, 대화상자, 팝업, 툴팁의 쌓임 순서를 정합니다.
원칙
- z-index 숫자 대신 이름 붙은 레이어를 쓰세요.
--surf-layer-*단계가 앱 크롬, 대화상자, 팝업, 툴팁을 한 순서로 묶습니다. - 떠 있는 것은 모두 포털하세요. 자식의 z-index는 부모의 쌓임 컨텍스트와 overflow 클리핑을 벗어나지 못합니다.
- 오버레이는 안쪽부터 닫으세요. Escape는 가장 안쪽 레이어부터 닫고 포커스를 그 트리거로 돌려줍니다.
- 숫자를 올리기 전에 진단하세요. 원인은 대개 포털 대상, 조상 컨텍스트, 클리핑이므로 숫자를 올려서는 잘 고쳐지지 않습니다.
레이어 단계
레이어는 그리는 순서만 정합니다. 떠 있는 면의 모양, 즉 그림자와 가장자리는 시각 위계에서 다룹니다.
Sheet와 Drawer는 격리된 Positioner 하나 안에서 배경에 0, 콘텐츠에 1을 씁니다. 메뉴와 토스트는 popup 레이어를 함께 쓰므로, 숫자가 같으면 DOM 순서가 정합니다.
쌓임 컨텍스트
자식의 z-index는 부모의 쌓임 컨텍스트를 벗어나지 못합니다. transform, 1보다 작은 opacity, isolation, 일부 contain 값이 컨텍스트를 만들므로, 콘텐츠 영역 전체에 transform을 넣지 마세요.
로컬: 자식은 부모 컨텍스트에 머무르므로 9999도 z-index가 2인 형제 컨텍스트를 앞서지 못합니다.
클리핑과 그리는 순서는 별개입니다. 클리핑을 꺼도 순서는 바뀌지 않습니다. 1, 2, 9999는 원리를 보여 주는 값이며 제품의 레이어 값이 아닙니다.
중첩 오버레이
Base UI가 초기 포커스, Tab 범위, 배경 차단, Escape 순서, 포커스 복귀를 맡습니다. 선택 상태와 작업 데이터는 호출하는 화면에 두세요.
진단
- DOM에서 실제 포털 대상과 조상 컨텍스트를 찾으세요. React 트리와 다를 수 있습니다.
- overflow, mask, clip-path로 잘린 것과 그리는 순서를 구분하세요.
- 레이어 토큰은 안쪽 콘텐츠가 아니라 팝업의 Positioner처럼 가장 바깥에 위치한 요소에 두세요.
- 브라우저의 top layer를 확인하세요. showModal() 대화상자, popover 속성, 전체 화면은 모든 z-index 위에 놓입니다. Surflane의 Dialog는 showModal()이 아니라 포털입니다.
참고 자료: W3C 쌓임 순서, React createPortal, W3C 위치 지정 레이아웃 / top layer, Base UI Dialog