scroll-snap-type: mandatory를 줬더니 스크롤이 갇힌 이유
문제 발생
가로 스크롤 카드 목록을 만들면서 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 헤더 문제는 다른 축입니다. 스냅 지점이 컨테이너의 가장자리를 기준으로 계산되므로, 그 가장자리를 가리는 고정 요소가 있으면 그만큼 가려집니다.
해결 방안
- 내용 크기를 통제할 수 없으면
proximity를 씁니다. 가까울 때만 붙고, 아니면 자유롭게 스크롤됩니다.
.carousel { scroll-snap-type: x proximity; }mandatory를 쓸 거면 자식 크기를 고정합니다. "한 화면에 한 장"이 보장될 때만 안전합니다.
.carousel { scroll-snap-type: x mandatory; }
.carousel > article {
flex: 0 0 100%;
scroll-snap-align: start;
}-
모든 자식에 정렬을 지정합니다. 빠뜨린 자식은
mandatory에서 접근 불가가 됩니다. -
고정 헤더가 있으면
scroll-padding으로 자리를 비웁니다. 컨테이너에 겁니다.
.scroller {
scroll-snap-type: y mandatory;
scroll-padding-top: 64px; /* sticky 헤더 높이 */
}- 자식마다 다르게 하려면
scroll-margin입니다. 컨테이너의scroll-padding과 함께 쓸 수 있습니다.
.scroller section { scroll-margin-block-start: 40px; }-
scroll-snap-stop: always는 신중하게 씁니다. 한 번에 여러 장을 건너뛰지 못하게 막아 모든 항목을 보게 하지만, 목록이 길면 원하는 곳까지 가는 데 오래 걸립니다. -
스냅은 스크롤을 대체하지 않습니다. 키보드와 스크린리더 사용자가 순서대로 접근할 수 있어야 하므로, 스냅에 의존해 콘텐츠를 감추지 않습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.