CSS 스크롤 스냅으로 잘 제어되는 스크롤

스크롤 고정 위치를 선언하여 잘 제어된 스크롤 환경을 만듭니다.

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

CSS Scroll Snap 기능을 사용하면 웹 개발자가 스크롤 고정 위치를 선언하여 잘 제어된 스크롤 환경을 만들 수 있습니다. 페이지로 나뉜 도움말과 이미지 캐러셀이 이와 관련하여 일반적으로 사용되는 두 가지 예입니다. CSS Scroll Snap은 이러한 인기 있는 UX 패턴을 빌드하기 위한 사용하기 쉽고 일관된 API를 제공합니다.

배경

스크롤 래핑의 사례

스크롤은 웹에서 콘텐츠와 상호작용하는 인기 있는 자연스러운 방법입니다. 스크롤은 한 번에 화면에 표시되는 것보다 더 많은 정보에 액세스할 수 있는 플랫폼의 기본 수단으로, 화면 공간이 제한된 모바일 플랫폼에서 특히 중요합니다. 따라서 웹 작성자가 점점 더 깊은 계층 구조가 아닌 스크롤 가능한 플랫 목록으로 콘텐츠를 구성하는 것을 선호하는 것은 당연합니다.

스크롤의 주요 단점은 정밀도가 떨어진다는 점입니다. 스크롤이 단락이나 문장에 정렬되는 경우는 거의 없습니다. 이는 페이지 또는 이미지의 중간에서 스크롤이 완료되어 콘텐츠가 부분적으로 표시되는 경우 의미 있는 경계가 있는 페이지로 나누어진 콘텐츠나 항목별로 나열된 콘텐츠에서 더욱 두드러집니다. 이러한 사용 사례에서는 잘 제어된 스크롤 환경을 활용할 수 있습니다.

웹 개발자는 오랫동안 스크롤을 제어하는 JavaScript 기반 솔루션을 사용하여 이 단점을 해결해 왔습니다. 그러나 JavaScript 기반 솔루션은 스크롤 맞춤설정 원시 요소가 없거나 컴포지션된 스크롤에 액세스할 수 없어 전체 충실도 솔루션을 제공하지 못합니다. CSS Scroll Snap은 브라우저에서 일관되게 작동하는 빠르고, 고화질이며, 사용하기 쉬운 솔루션을 보장합니다.

CSS 스크롤 스냅을 사용하면 웹 작성자가 각 스크롤 컨테이너를 스크롤 작업이 완료되는 경계로 표시할 수 있습니다. 그런 다음 브라우저는 스크롤 작업의 세부정보, 스크롤 컨테이너의 레이아웃 및 표시 상태, 스냅 위치의 세부정보에 따라 가장 적절한 종료 위치를 선택한 다음 원활하게 애니메이션을 적용합니다. 이전 예로 돌아가면 사용자가 캐러셀 스크롤을 완료하면 표시된 이미지가 제자리에 고정됩니다. JavaScript에서 스크롤 조정이 필요하지 않습니다.

이미지 캐러셀과 함께 CSS 스크롤 스냅을 사용하는 예시
이미지 캐러셀에 CSS 스크롤 스냅을 사용하는 예시입니다. 여기서 스크롤 래핑은 스크롤이 끝날 때 이미지의 가로 가운데가 스크롤 컨테이너의 가로 가운데와 정렬되도록 합니다.

CSS 스크롤 스냅

스크롤 스냅은 스크롤 작업이 완료된 후 스크롤 컨테이너의 스크롤 오프셋을 선호하는 스냅 위치로 조정하는 작업입니다.

스크롤 컨테이너는 scroll-snap-type 속성을 사용하여 스크롤 고정을 선택할 수 있습니다. 이렇게 하면 브라우저에 이 스크롤 컨테이너를 하위 요소에서 생성된 스냅 위치에 스냅하는 것이 좋음을 알립니다. scroll-snap-type는 스크롤이 발생하는 축(x, y 또는 both)과 스냅 엄격도(mandatory, proximity)를 결정합니다. 이 내용은 나중에 자세히 설명하겠습니다.

요소에서 원하는 정렬을 선언하여 스냅 위치를 만들 수 있습니다. 이 위치는 가장 가까운 상위 스크롤 컨테이너와 요소가 지정된 축에 지정된 대로 정렬되는 스크롤 오프셋입니다. 각 축에서 다음과 같은 정렬이 가능합니다. start, end, center

start 정렬은 스크롤 컨테이너 스냅 포트 시작 가장자리가 요소 스냅 영역 시작 가장자리와 정렬되어야 함을 의미합니다. 마찬가지로 endcenter 정렬은 스크롤 컨테이너 스냅 포트 끝 가장자리 또는 중앙이 요소 스냅 영역 끝 가장자리 또는 중앙과 정렬되어야 함을 의미합니다.

가로 스크롤 축의 다양한 정렬 예시입니다.

다음 예는 이러한 개념을 사용하는 방법을 보여줍니다.

스크롤 스냅의 일반적인 사용 사례는 이미지 캐러셀입니다. 예를 들어 스크롤할 때 각 이미지에 맞게 스냅되는 가로 이미지 캐러셀을 만들려면 스크롤 컨테이너가 가로축에 필수 scroll-snap-type를 갖도록 지정할 수 있습니다. 각 이미지를 scroll-snap-align: center로 설정하여 스냅이 캐러셀 내에서 이미지를 가운데에 배치하도록 합니다.

#gallery {
  scroll-snap-type: x mandatory;
  overflow-x: scroll;
  display: flex;
}

#gallery img {
   scroll-snap-align: center;
}
<div id="gallery">
  <img src="cat.jpg">
  <img src="dog.jpg">
  <img src="another_cute_animal.jpg">
</div>

고정 위치는 요소와 연결되므로 고정 알고리즘은 요소와 스크롤 컨테이너 크기를 고려하여 고정 시점과 방법을 스마트하게 결정할 수 있습니다. 예를 들어 하나의 이미지가 캐러셀보다 큰 경우를 생각해 보겠습니다. 단순한 스냅 알고리즘은 사용자가 화면을 이동하여 전체 이미지를 볼 수 없게 할 수 있습니다. 그러나 사양에 따라 구현은 이 사례를 감지하고 사용자가 이미지 내에서 자유롭게 스크롤하면서 가장자리에서만 이미지를 맞추도록 허용해야 합니다.

데모 보기 | 소스

예: 여정을 거친 제품 페이지

스크롤 래핑의 이점을 누릴 수 있는 또 다른 일반적인 사례는 세로로 스크롤할 수 있는 여러 개의 논리적 섹션이 있는 페이지(예: 일반적인 제품 페이지)입니다. scroll-snap-type: y proximity;는 이와 같은 경우에 더 적합합니다. 사용자가 특정 섹션의 중앙으로 스크롤할 때는 방해가 되지 않지만 충분히 가까이 스크롤하면 새 섹션으로 이동하여 사용자의 주의를 끕니다.

방법은 다음과 같습니다.

article {
  scroll-snap-type: y proximity;
  /* Reserve space for header plus some extra space for sneak peeking. */
  scroll-padding-top: 15vh;
  overflow-y: scroll;
}
section {
  /* Snap align start. */
  scroll-snap-align: start;
}
header {
  position: fixed;
  height: 10vh;
}
<article>
  <header> Header </header>
  <section> Section One </section>
  <section> Section Two </section>
  <section> Section Three </section>
</article>

스크롤 패딩 및 여백

제품 페이지에 고정된 상단 헤더가 있습니다. 또한 디자인에서는 스크롤 컨테이너가 스냅될 때 상단 콘텐츠에 관한 디자인 신호를 사용자에게 제공하기 위해 상단 섹션의 일부가 계속 표시되도록 요청했습니다.

scroll-padding 속성은 스크롤 컨테이너 또는 스냅 포트의 효과적인 표시 영역을 조정하는 데 사용할 수 있는 새로운 CSS 속성으로, 스크롤 스냅 정렬을 계산할 때 사용됩니다. 이 속성은 스크롤 컨테이너의 패딩 상자에 대한 인셋을 정의합니다. 이 예에서는 상단에 15vh 추가 인셋이 추가되어 브라우저에 스크롤 컨테이너의 상단 가장자리 아래에 있는 더 낮은 위치(15vh)를 스크롤 스냅의 세로 시작 가장자리로 간주하도록 지시합니다. 스냅할 때 스냅 대상 요소의 시작 가장자리가 이 새 위치와 정렬되어 위쪽에 공간이 남게 됩니다.

scroll-margin 속성은 스냅 스크롤 컨테이너에서 scroll-padding가 작동하는 방식과 유사하게 스냅 타겟 효과 상자를 조정하는 데 사용되는 시작 금액을 정의합니다.

이 두 속성에는 'snap'라는 단어가 포함되어 있지 않습니다. 이는 스크롤 스냅이 아니라 모든 관련 스크롤 작업의 상자를 실제로 수정하기 때문에 의도된 동작입니다. 예를 들어 Chrome은 PageDown 및 PageUp과 같은 페이징 스크롤 작업의 페이지 크기를 계산할 때, 그리고 Element.scrollIntoView() 작업의 스크롤 양을 계산할 때 이를 고려합니다.

데모 보기 | 소스

다른 스크롤 API와의 상호작용

DOM Scrolling API

스크롤 고정은 스크립트에서 시작한 스크롤 작업을 비롯한 모든 스크롤 작업 후에 발생합니다. Element.scrollTo와 같은 API를 사용하면 브라우저가 작업의 의도된 스크롤 위치를 계산한 다음 적절한 스냅 로직을 적용하여 최종 스냅된 위치를 찾습니다. 따라서 사용자 스크립트에서 수동으로 스냅 계산을 할 필요가 없습니다.

부드러운 스크롤

원활한 스크롤은 프로그래매틱 스크롤 작업의 동작을 제어하고 스크롤 스냅은 대상을 결정합니다. 스크롤의 직교적 측면을 제어하므로 함께 사용하고 서로 보완할 수 있습니다.

오버스크롤 동작

Overscroll behavior API는 여러 요소에 걸쳐 스크롤이 연결되는 방식을 제어하며 스크롤 스냅의 영향을 받지 않습니다.

주의사항 및 권장사항

타겟 요소 간의 간격이 넓은 경우 강제 스냅을 사용하지 마세요. 이로 인해 스냅 위치 사이의 콘텐츠에 액세스할 수 없게 될 수 있습니다.

대부분의 경우 특징 감지 없이 스크롤 스냅을 개선사항으로 추가할 수 있습니다. 필요한 경우 @supports 또는 CSS.supports를 사용하여 CSS Scroll Snap 지원을 감지합니다. 지원 중단된 사양에도 있는 scroll-snap-type는 사용하지 마세요.

CSS의 기능 감지

@supports (scroll-snap-align: start) {
  article {
    scroll-snap-type: y proximity;
    scroll-padding-top: 15vh;
    overflow-y: scroll;
  }
}

JavaScript의 기능 감지

if (CSS.supports('scroll-snap-align: start')) {
  // use css scroll snap
} else {
  // use fallback
}

Element.scrollTo와 같은 프로그래매틱 스크롤 API가 항상 요청된 스크롤 오프셋에서 완료된다고 가정하지 마세요. 스크롤 래핑은 프로그래매틱 스크롤이 완료된 후 스크롤 오프셋을 조정할 수 있습니다. 스크롤이 다른 이유로 중단되었을 수 있으므로 스크롤 스냅 이전에도 이는 좋은 가정이 아니었지만, 스크롤 스냅의 경우 특히 그렇습니다.

향후 작업

스크롤 환경은 Chrome팀의 최근 설문조사의 주제였습니다. 설문조사 결과 플러그인 라이브러리와 CSS 간의 격차를 줄이기 위해 추가 작업이 필요한 몇 가지 영역이 확인되었습니다. 향후 작업에서는 다음을 포함하여 scroll-snap에 중점을 둘 예정입니다.

  1. 브라우저 간 API 사용 가능 여부 및 호환성
  2. scroll-start와 같은 새 CSS API를 사용합니다.
  3. snapChanged()와 같은 새 JS 이벤트를 사용합니다.