スクロールスナップ
問題
カルーセル、スライドショー、水平スクロールコンテンツセクションは非常に一般的なUIパターンです。AIエージェントはスナップ・トゥ・スライド動作の実装にほぼ必ずJavaScriptライブラリやカスタムスクロールイベントハンドラーを使おうとします。CSS Scroll Snap はこの機能をわずか数行のCSSでネイティブに提供し、JavaScriptソリューションよりも優れたパフォーマンスとタッチデバイス互換性を実現します。しかし、AIがこれを提案することはまれです。
解決方法
CSS Scroll Snap を使用すると、スクロールコンテナ上にスナップポイントを定義でき、スクロールが自然に特定の位置にロックされます。ブラウザがモーメンタム、減速、スナップなどすべての物理演算を処理するため、すべてのデバイスでスムーズなネイティブ感覚のスクロール動作を実現します。
主要プロパティ
scroll-snap-type(スクロールコンテナに設定):スナップの軸(x、y、both)と厳密さ(mandatoryまたはproximity)を定義します。scroll-snap-align(子要素に設定):各アイテムがスナップする位置(start、center、end)を定義します。scroll-snap-stop(子要素に設定):スクロールがアイテムを飛ばせるか(normal)、各アイテムで停止しなければならないか(always)を制御します。
mandatory と proximity
mandatory:スクロールが停止すると、スクロールコンテナは常にスナップポイントにスナップします。ユーザーがわずかにスクロールしただけでも、最も近いポイントにスナップします。カルーセルやページネーションコンテンツに最適です。proximity:スナップポイントの近くにいる場合のみスナップします。ユーザーがスナップポイントを通過してスクロールすると、通常のスクロールのように動作します。スナップが便利だが必須ではない長いコンテンツに最適です。
水平スクロールスナップカルーセル
コード例
水平カルーセル
.carousel {
display: flex;
gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
/* Hide scrollbar but keep functionality */
scrollbar-width: none;
}
.carousel::-webkit-scrollbar {
display: none;
}
.carousel__slide {
flex: 0 0 100%;
scroll-snap-align: start;
}
<div class="carousel">
<div class="carousel__slide">Slide 1</div>
<div class="carousel__slide">Slide 2</div>
<div class="carousel__slide">Slide 3</div>
</div>
マルチアイテムカルーセル(次のアイテムをチラ見せ)
.carousel-peek {
display: flex;
gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-padding-inline: 1rem;
padding-inline: 1rem;
}
.carousel-peek__item {
flex: 0 0 calc(80% - 0.5rem);
scroll-snap-align: start;
border-radius: 0.5rem;
background: var(--color-surface, #f5f5f5);
padding: 1.5rem;
}
コンテナの scroll-padding-inline により、スナップされたアイテムが端からオフセットされ、次のアイテムがチラ見えします。
縦方向フルページセクション
.page-sections {
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
}
.section {
height: 100vh;
scroll-snap-align: start;
display: flex;
align-items: center;
justify-content: center;
}
<div class="page-sections">
<section class="section">Section 1</section>
<section class="section">Section 2</section>
<section class="section">Section 3</section>
</div>
中央スナップの画像ギャラリー
.gallery {
display: flex;
gap: 0.5rem;
overflow-x: auto;
scroll-snap-type: x proximity;
padding-block: 1rem;
}
.gallery__image {
flex: 0 0 auto;
width: min(300px, 80vw);
aspect-ratio: 3 / 4;
object-fit: cover;
border-radius: 0.5rem;
scroll-snap-align: center;
}
ここでは proximity を使用することで、穏やかなセンタースナップ動作を持つ自由なブラウジングが可能になります。
高速スクロールスキップの防止
.carousel-strict {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.carousel-strict__slide {
flex: 0 0 100%;
scroll-snap-align: start;
scroll-snap-stop: always; /* Must stop at every slide */
}
scroll-snap-stop: always により、ユーザーが一度に複数のスライドをスワイプで飛ばすことを防止します。
レスポンシブ対応:カルーセルからグリッドへ
.card-scroller {
display: flex;
gap: 1rem;
overflow-x: auto;
scroll-snap-type: x mandatory;
padding: 1rem;
}
.card-scroller__item {
flex: 0 0 min(280px, 85vw);
scroll-snap-align: start;
}
/* On wider screens, switch to a grid layout */
@media (min-width: 48rem) {
.card-scroller {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
overflow-x: visible;
scroll-snap-type: none;
}
.card-scroller__item {
scroll-snap-align: unset;
}
}
AIがよくやるミス
- Scroll Snap を提案しない:CSSがネイティブで処理できるスクロールスナップ動作にJavaScriptカルーセルライブラリをデフォルトで使用します。
- コンテナに
scroll-snap-typeを忘れる:子要素にscroll-snap-alignを設定しながら、親でスナップを有効にしていません。 - 常に
mandatoryを使用する:proximityの方が適切な長いスクロールコンテンツにmandatoryを使用します。長いコンテンツでのmandatoryはユーザーを閉じ込めてしまう可能性があります。 scroll-paddingを使用しない:固定ヘッダーがあるページでscroll-paddingを追加し忘れ、スナップされたコンテンツがヘッダーの背後に隠れてしまいます。- アクセシビリティを維持せずにスクロールバーを非表示にする:CSSでスクロールバーを削除しながら、代替のナビゲーション(矢印、ドット)を提供しません。
scroll-snap-stopを使用しない:ステップバイステップのコンテンツ(オンボーディングフローなど)で、スキップを防止するためのscroll-snap-stop: alwaysを使用していません。- スライドアイテムに固定ピクセル幅を使用する:
min(300px, 85vw)のようなレスポンシブサイジングの代わりにflex: 0 0 350pxを使用します。
使い分け
- カルーセルとスライドショー:フル幅の画像カルーセル、テスティモニアルスライダー、製品ショーケースに使います。
- 水平スクロールセクション:カードスクローラー、カテゴリナビゲーション、画像ギャラリーに使います。
- フルページセクションスクロール:縦方向にスナップする個別のセクションを持つランディングページに使います。
- オンボーディングフロー:各ステップがビューにスナップするべきステップバイステップの画面に使います。
- 複雑なカルーセルロジックには不向き:自動再生、無限ループ、APIドリブンのスライド管理が必要な場合はJavaScriptが必要になるかもしれませんが、スクロールスナップ動作自体はCSSドリブンのままにすべきです。