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

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

Robert Flack
Robert Flack
Majid Valipour
Majid Valipour

CSS 스크롤 스냅 기능을 사용하면 웹 개발자가 스크롤 맞추기 위치를 선언하여 잘 제어되는 스크롤 환경을 만들 수 있습니다. 페이지로 나눈 기사와 이미지 캐러셀이 흔히 사용되는 두 가지 예입니다. CSS 스크롤 스냅은 이러한 인기 있는 UX 패턴을 빌드할 수 있는 사용하기 쉽고 일관된 API를 제공합니다.

배경

스크롤 래핑의 사례

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

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

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

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 스크롤 API

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

부드러운 스크롤

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

오버스크롤 동작

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

주의사항 및 권장사항

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

대부분의 경우 특징 감지 없이 스크롤 스냅을 개선사항으로 추가할 수 있습니다. 필요한 경우 @supports 또는 CSS.supports를 사용하여 CSS 스크롤 스냅 지원을 감지합니다. 지원 중단된 사양에도 있는 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 이벤트를 사용합니다.