scroll-snap で JS なしの「カード単位で止まる」横スクロールスライダー
横スクロールの親に scroll-snap-type: x mandatory、各カードに scroll-snap-align: start を付けるだけで、スワイプやトラックパッドでスクロールしたときにカードの端でピタッと止まる。
どんな時に使う?
- スマホで横スワイプするカード一覧・商品カルーセル・画像ギャラリーを、スライダー用ライブラリなしで作りたい時
- 横スクロールの途中半端な位置で止まって、カードが見切れたままになるのを防ぎたい時
コード
html
<div class="slider">
<div class="card">カード1</div>
<div class="card">カード2</div>
<div class="card">カード3</div>
</div>css
.slider {
display: flex;
gap: 16px;
overflow-x: auto;
scroll-snap-type: x mandatory; /* 横方向に必ず吸着させる */
scroll-padding-inline: 16px; /* 吸着位置を左端から 16px 内側にずらす */
overscroll-behavior-x: contain; /* 端まで行ってもページ側の戻る操作などに伝えない */
}
.card {
flex: 0 0 80%; /* 1枚を 80% 幅にして次のカードを少し覗かせる */
scroll-snap-align: start; /* カードの左端で止める(center にすると中央で止まる) */
}scroll-snap-typeはスクロールする親に、scroll-snap-alignは止まらせたい子に付ける。2つ揃って初めて効く。mandatoryはスクロールが止まったら必ずどこかのカードに吸着、proximityは吸着点の近くで止まった時だけ吸着する。- 1回のスワイプで何枚も飛ばされたくない時は、カードに
scroll-snap-stop: alwaysを付けると1枚ずつ止まる。
注意点・対応状況
- 主要ブラウザすべてで使える。
mandatoryで、カードがスクロール領域より大きい(縦長の中身など)場合、中身の途中まで読めずに次の吸着点へ飛ばされることがある。そういう時はproximityにする。- キーボードでも操作できるよう、スクロール領域に
tabindex="0"とaria-labelを付けておくとよい。 - 横スクロールのスクロールバーの出方を揃えたい場合は、テーブルの例だが 横スクロールするテーブルはラッパーで包む(table に overflow-x は効かない) も参考になる。
- 元の投稿は
scroll-snap-typeとscroll-snap-alignだけの最小例。scroll-padding-inline・overscroll-behavior-x・scroll-snap-stopは補足として加えたもの。