본문으로 건너뛰기
개발 머꼬
개발 노트CSS
hohyeon.dev15

scroll-snap-type: mandatory를 줬더니 스크롤이 갇힌 이유

  • #Common Pitfall
  • #CSS
  • #Engineering Note

문제 발생

가로 스크롤 카드 목록을 만들면서 scroll-snap-type: x mandatory를 줬더니, 화면보다 넓은 카드 하나에서 스크롤이 갇혔습니다. 그 카드의 오른쪽 절반을 볼 방법이 없었습니다.

.carousel { scroll-snap-type: x mandatory; }
.carousel > article { scroll-snap-align: start; }

세로 목록에서는 sticky 헤더 뒤로 제목이 숨었습니다.

원인 분석

mandatory는 이름 그대로 반드시 스냅 지점에 붙입니다. 그래서 스냅 지점 사이에 있는 위치에서는 머무를 수 없습니다. 자식이 컨테이너보다 크면 그 안쪽을 볼 수 없게 됩니다.

MDN이 이걸 경고로 못박습니다 — 자식 요소의 내용이 부모 컨테이너를 넘칠 경우 mandatory를 절대 쓰지 마십시오. 사용자가 넘친 내용을 스크롤해서 볼 수 없게 됩니다. 그리고 화면에 얼마나 많은 내용이 있는지 알고 있는 상황에서만 신중히 쓰라고 덧붙입니다.

같은 이유로 함정이 하나 더 있습니다 — mandatory인데 어떤 자식에 scroll-snap-align이 없으면(기본값 none), 그 요소를 화면에 띄울 수 없습니다.

sticky 헤더 문제는 다른 축입니다. 스냅 지점이 컨테이너의 가장자리를 기준으로 계산되므로, 그 가장자리를 가리는 고정 요소가 있으면 그만큼 가려집니다.

해결 방안

  1. 내용 크기를 통제할 수 없으면 proximity를 씁니다. 가까울 때만 붙고, 아니면 자유롭게 스크롤됩니다.
.carousel { scroll-snap-type: x proximity; }
  1. mandatory를 쓸 거면 자식 크기를 고정합니다. "한 화면에 한 장"이 보장될 때만 안전합니다.
.carousel { scroll-snap-type: x mandatory; }
.carousel > article {
  flex: 0 0 100%;
  scroll-snap-align: start;
}
  1. 모든 자식에 정렬을 지정합니다. 빠뜨린 자식은 mandatory에서 접근 불가가 됩니다.

  2. 고정 헤더가 있으면 scroll-padding으로 자리를 비웁니다. 컨테이너에 겁니다.

.scroller {
  scroll-snap-type: y mandatory;
  scroll-padding-top: 64px;   /* sticky 헤더 높이 */
}
  1. 자식마다 다르게 하려면 scroll-margin입니다. 컨테이너의 scroll-padding과 함께 쓸 수 있습니다.
.scroller section { scroll-margin-block-start: 40px; }
  1. scroll-snap-stop: always는 신중하게 씁니다. 한 번에 여러 장을 건너뛰지 못하게 막아 모든 항목을 보게 하지만, 목록이 길면 원하는 곳까지 가는 데 오래 걸립니다.

  2. 스냅은 스크롤을 대체하지 않습니다. 키보드와 스크린리더 사용자가 순서대로 접근할 수 있어야 하므로, 스냅에 의존해 콘텐츠를 감추지 않습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.