본문으로 건너뛰기
wooncloud

sticky 가 조용히 죽는 자리, overflow:hidden 은 스크롤하지 않아도 scrollport 다

같은 컴포넌트인데 iframe 화면에서만 position:sticky 가 먹지 않았던 원인은 루트의 overflow:hidden 이었습니다. 스크롤 컨테이너 기준과 flex 높이 협상의 함정까지 다룹니다.

·5 min read·— views··

목록 위에 붙는 액션 바를 position: sticky; top: 0 으로 고정했는데, 같은 컴포넌트가 한 화면에서는 잘 붙고 다른 화면에서는 스크롤하면 그대로 밀려 올라가 사라졌습니다.

잘 되는 쪽은 일반 페이지였고, 안 되는 쪽은 iframe 으로 다른 서비스에 끼워 넣은 화면이었습니다. 마크업도 스타일도 똑같은데 말이죠.

처음 세운 가설은 틀렸습니다

"iframe 바깥(호스트 페이지)이 스크롤하니까 iframe 안의 sticky 는 원리적으로 못 붙는다"고 생각했습니다. 그럴듯하지만 틀렸습니다.

실제 원인은 임베드용 셸 루트에 있던 한 줄이었습니다.

/* 임베드 셸 루트 */
min-height: 100vh;
overflow: hidden;   /* ← 이것 */

overflow 가 visible 이 아닌 요소는 스크롤이 실제로 일어나는지와 상관없이 scroll container(scrollport)가 됩니다. sticky 는 가장 가까운 스크롤 컨테이너를 기준으로 동작하니, 이 div 가 기준이 됩니다.

그런데 이 div 는 높이가 콘텐츠만큼 자라 있어서 자기 안에서는 스크롤이 일어나지 않습니다. 기준 축이 한 번도 움직이지 않으니 sticky 는 아무 일도 하지 않고, 정작 스크롤되는 건 그 바깥(문서)이라 바가 통째로 밀려 올라갑니다.

잘 되던 쪽에는 중간에 overflow: auto 인 진짜 스크롤 컨테이너가 한 겹 있었습니다. 둘의 유일한 차이였습니다.

스크롤 축을 가진 요소를 직접 둡니다

sticky 를 쓰는 화면에서는 스크롤 축을 소유한 요소를 명시적으로 둡니다.

<div style="height:100%; display:flex; flex-direction:column; overflow:hidden">
  <main style="flex:1; min-height:0; overflow:auto">
    <div style="position:sticky; top:0">액션 바</div>
    <!-- 목록 -->
  </main>
</div>

min-height: 0 이 빠지면 flex 아이템의 기본값(auto)이 축소를 막아서 스크롤이 생기지 않습니다. 자주 빠뜨리는 짝이라 같이 챙겨야 합니다.

곁다리, iframe 높이를 알려 주는 프로토콜은 반영을 보장하지 않습니다

이 화면은 콘텐츠 높이가 바뀔 때마다 postMessage 로 높이를 호스트에 알리고, 호스트가 iframe.style.height 를 설정하는 구조였습니다. 그래서 프레임이 콘텐츠만큼 길어져 있다고 가정했는데, 직접 재 보니 아니었습니다.

호스트의 컨테이너가 flex 였고 iframe 에 flex: 1 1 auto 가 걸려 있었습니다. flex-shrink: 1 이라 명시한 height 가 컨테이너 높이로 되돌아옵니다. 5,440px 을 요청했는데 실제로는 640px 이었습니다.

높이 협상 프로토콜이 있다고 해서 그 값이 적용되는 건 아닙니다. flex/grid 컨테이너 안에서 height 는 결정이 아니라 요청입니다.

그리고 이런 판단은 읽어서 추론하지 말고 재야 합니다. 호스트의 CSS 체인을 복제한 페이지를 만들어 헤드리스 브라우저로 세 가지 변형을 나란히 측정했습니다. 스크롤 600px 뒤 액션 바의 화면 좌표는 이랬습니다.

변형액션 바 좌표
현행-520px
overflow 제거80px
스크롤 컨테이너 도입80px

20분짜리 프로브가 가설 하나를 뒤집었네요.

참고