CSSのスクロールスナップの便利な使い方、実装の注意点を徹底解説
Post on:2020年12月22日
CSSのスクロールスナップが登場して早4年、現在ではほぼすべてのブラウザにサポートされ、採用しているWebサイトやスマホアプリも増えてきました。
CSSのスクロールスナップについて、基礎知識をはじめ、各プロパティの機能や使い方、スクロールスナップの実際の使用例、実装の注意点などを紹介します。
CSS Scroll Snap
by Ahmad Shadeed
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- CSSのスクロールスナップを使う理由
- スクロールコンテナの基本
- スクロール コンテナの注意点
- CSSのスクロールスナップとは
- scroll-snap-stopの使い方
- scroll-paddingの使い方
- scroll-marginの使い方
- CSSのスクロールスナップの使用例
- block値とinline値について
- アクセシビリティ
- 終わりに
はじめに
スクロール可能なコンテナを簡単に実装できるCSSの機能があったらと、望んだことがあると思います。CSSのスクロールスナップはそれを簡単に実現できます。
フロントエンド開発の初期の頃は、スライダーを実装するためにJavaScriptのプラグインに頼っていました。また、要素をスクロール可能なコンテナとしてすばやく実装する方法が必要になることもありました。
今日ではCSSのスクロールスナップのおかげで、それらが全部可能です。
スクロールスナップは現在、ほぼすべてのブラウザでサポートされています。
この記事では、CSSのスクロールスナップの基本から実際の使用方法までていねいに解説したいと思います。
準備はいいですか? さぁ始めましょう!
CSSのスクロールスナップを使う理由
スマホやタブレットの台頭に伴い、タッチ操作でスワイプできるコンポーネントを設計・構築する必要があります。
例えば、ギャラリーを例に考えてみましょう。ユーザーは階層構造よりも、左右のスワイプでより多くの画像を簡単に見ることができます。
タッチデバイスでは、左右のスワイプの方が簡単
CSSの仕様によると、デベロッパーによって適切に制御されたスクロールエクスペリエンスを提供することは、CSSのスクロールスナップが導入された理由の1つです。ユーザーエクスペリエンスを向上させるスクロールエクスペリエンスの実装が容易になります。
スクロールコンテナの基本
スクロールコンテナを作成するために必要なものは次のとおりです。
- overflowプロパティをvisible以外の値で使用する。
- アイテムを横に並べて(インラインで)表示する方法。
例を見てみましょう。
1 2 3 4 5 6 7 |
<div class="section"> <div class="section__item">Item 1</div> <div class="section__item">Item 2</div> <div class="section__item">Item 3</div> <div class="section__item">Item 4</div> <div class="section__item">Item 5</div> </div> |
1 2 3 4 |
.section { white-space: nowrap; overflow-x: auto; } |
white-space: nowrap;はここ何年のもの間、要素を強制的にインラインにするための一般的なCSSのテクニックでした。しかし、このような使い方は本来のものとは異なるため、望んでいないでしょう。現在ではありがたいことに、これを回避して代わりにFlexboxで実現できます。
1 2 3 4 |
.section { display: flex; overflow-x: auto; } |
スクロールコンテナ
これはスクロールコンテナを作成するための基本的なコードです。とはいえ、これだけでは不十分で、まだ使い物にはなりません。
スクロール コンテナの注意点
スクロール コンテナの注意点はスワイプがどのように動作するかに比べて、優れたエクスペリエンスが提供されないことです。タッチスクリーンでのスワイプジェスチャーの主な利点は、1本の指で水平または垂直にスクロールできることです。
何もしないと、下記のような操作を強いられます。
各アイテムをそれぞれの場所に指で移動させるのはつらい
文字通り、各アイテムをそれぞれの場所に指で移動させる必要があります。これはスワイプと呼べるものではなく、非常に悪いエクスペリエンスです。CSSのスクロールスナップを使用することで、ユーザーが水平または垂直方向にスクロールしやすくなるスナップポイントを定義するだけで、この問題を解決できます。
CSSのスクロールスナップの使用方法を見てみましょう。
CSSのスクロールスナップとは
コンテナ上でスクロールスナップを使用するには、その子アイテムをインラインで表示する必要があり、これは前述した方法で実装できます。nowrapは古いテクニックなので、display: flex;を使用して実装します。
1 2 3 4 5 6 7 |
<div class="section"> <div class="section__item">Item 1</div> <div class="section__item">Item 2</div> <div class="section__item">Item 3</div> <div class="section__item">Item 4</div> <div class="section__item">Item 5</div> </div> |
1 2 3 4 |
.section { display: flex; overflow-x: auto; } |
スクロールスナップを機能させるためには、さらに2つのプロパティを追加する必要があります。どこに追加すればいいのか? いい質問ですね。
まず、スクロールコンテナにscroll-snap-typeを追加します。この例では、.section要素がスクロールコンテナです。次に、子アイテムの.section__itemにscroll-snap-alignを追加します。
1 2 3 4 5 6 7 8 9 |
.section { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; } .section__item { scroll-snap-align: start; } |
x mandatoryとstartという値が気になるかもしれません。心配しないでください。これらが核心なので、後ほど詳しく解説します。
2つのプロパティを追加したことにより、開始点にスナップするスクロールコンテナが作成できました。
開始点にスナップするスクロールコンテナの完成
これを初めてみた時、私は興奮しました! スクロールがより自然になります。
では、スクロールスナップのプロパティについて詳しく解説します。
scroll-snap-typeプロパティとは
CSSの仕様によると、scroll-snap-typeとは要素がスクロールスナップコンテナかどうか、どの程度厳密にスナップするか、どの軸を考慮するかなどを定義するプロパティです。
スクロールスナップコンテナの軸とは、スクロールの方向を表します。水平または垂直にすることができ、x値は水平スクロール、y値は垂直スクロールを表します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/* 水平スクロール */ .section { display: flex; overflow-x: auto; scroll-snap-type: x; } /* 垂直スクロール */ .section { height: 250px; overflow-y: auto; scroll-snap-type: y; } |
x値は水平スクロール、y値は垂直スクロール
スクロールスナップコンテナの厳密さとは、scroll-snap-typeプロパティのもう1つの値としてmandatory | proximityを定義できます。
mandatory値は、ブラウザが各スクロールポイントにスナップする必要があることを意味します。scroll-snap-alignプロパティの値がstartであると仮定します。この時は、スクロールはスクロールコンテナの開始点にスナップしなければならないということです。
下記は、ユーザーが右にスクロールするたびに、ブラウザがアイテムをコンテナの開始点にスナップします。
mandatory値を使用した場合
1 2 3 4 5 6 7 8 9 |
.section { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; } .section__item { scroll-snap-align: start; } |
mandatory値を使用した場合
さらに良いことに、下記のデモで右にスクロールしてみてください。スマホやタブレットなど、タッチ操作ができる環境でご覧ください。あなたは、各アイテムがコンテナの先頭にどのようにスナップするか体験できます。
See the Pen
Scroll Snap Strictness by Ahmad Shadeed (@shadeed)
on CodePen.
しかし、値がproximityの場合はブラウザが仕事をします。定義されたポイントにスナップする可能性があります(この場合は開始点)。proximity値がデフォルト値なことに注意してください。
proximity値を使用した場合
1 2 3 4 5 6 |
.section { display: flex; overflow-x: auto; /* proximityがデフォルト値です。 */ scroll-snap-type: x proximity; } |
proximity値を使用した場合
スクロールスナップの位置
スクロールコンテナの子アイテムには、スナップできる位置合わせポイントが必要です。start, center, endのいずれかを定義できます。
これらがどのように機能するかを視覚的に示しました。
start, center, endの位置(水平の場合)
スクロールコンテナに磁石があり、スナップする位置を制御しているイメージです。scroll-snap-typeがyで垂直の場合は、スナップの位置も垂直になります。
start, center, endの位置(垂直の場合)
start, center, endの位置を明確にするために、下記をご覧ください。
スクロールコンテナのstartの位置
子アイテムは、スクロールコンテナの開始点にスナップします。
スクロールコンテナのstartの位置
スクロールコンテナのcenterの位置
子アイテムは、スクロールコンテナの中央にスナップします。
スクロールコンテナのcenterの位置
スクロールコンテナのendの位置
子アイテムは、スクロールコンテナの終点にスナップします。
スクロールコンテナのendの位置
scroll-snap-stopの使い方
時には、スクロール中にユーザーが誤って重要なアイテムをスキップしてしまうのを防ぐ方法が必要になるかもしれません。ユーザーのスクロールが速いと、いくつかのアイテムをスキップしてしまう可能性があります。
1 2 3 4 |
.section__item { scroll-snap-align: start; scroll-snap-stop: normal; } |
スクロールが速いと、アイテムをスキップする可能性がある
scroll-snap-stopのデフォルト値は、normalです。すべてのポイントにスクロールを可能な限りスナップさせるには、alwaysを定義します。
scroll-snap-stop: always;にすると、各スナップポイントで停止します。
1 2 3 4 |
.section__item { scroll-snap-align: start; scroll-snap-stop: always; } |
always値は、各スナップポイントで停止する
always値を定義することで、ユーザーは一度に1つのスナップポイントまでスクロールすることができ、重要なアイテムをスキップすることを避けることができます。各スナップポイントにストップのサインがあると想像してみてください。
always値は、各スナップポイントにストップのサインがあるイメージ
normalとalwaysの挙動の違いは、下記のデモをご覧ください。
See the Pen
Scroll Snap Stop by Ahmad Shadeed (@shadeed)
on CodePen.
scroll-paddingの使い方
scroll-paddingはショートハンドのプロパティで、paddingと同様に上下左右にスクロールのパディングを定義します。下記ではスクロールコンテナの左側に50pxのパディングを与えており、その結果子要素は左端から50pxにスナップします。
1 2 3 4 5 |
.section { overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding: 0 0 0 50px; } |
scroll-paddingの使用例(水平)
垂直のスクロールでも同様に機能します。
1 2 3 4 5 |
.section { overflow-y: auto; scroll-snap-type: y mandatory; scroll-padding: 50px 0 0 0; } |
scroll-paddingの使用例(垂直)
scroll-marginの使い方
scroll-marginもショートハンドのプロパティで、スクロールコンテナの子アイテム間のスペースを定義します。要素にマージンが追加されると、スクロールはマージンに合わせてスナップします。
scroll-marginの使用例
.item-2にscroll-margin-left: 20px;を定義しています。その結果、スクロールコンテナはそのアイテムより前20pxにスナップします。ユーザーが再び右にスクロールすると、.item-3は開始点にスナップします。
つまり、マージンのある要素のみが影響を受けることを意味します。
CSSのスクロールスナップの使用例
画像のリスト
CSSのスクロールスナップの優れた使用例は、画像のリストです。スクロールスナップを使用すると、はるかに優れたスクロールエクスペリエンスが得られます。
画像のリスト
1 2 3 4 5 6 7 8 9 10 11 |
.images-list { display: flex; overflow-x: auto; scroll-snap-type: x; gap: 1rem; -webkit-overflow-scrolling: touch; /* Important for iOS devices */ } .images-list img { scroll-snap-align: start; } |
scroll-snap-typeの値に、xを定義したことに注目してください。スナップの厳密さはproximityです。
See the Pen
Scroll Snap - Images List by Ahmad Shadeed (@shadeed)
on CodePen.
友達のリスト
CSSのスクロールスナップのもう1つの優れた使用例は、友達のリストです。こちらは画像のみではなく、カードが使用されています。以下の例は、Facebookの使用例です。
友達のリスト
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.list { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; gap: 1rem; scroll-padding: 48px; padding-bottom: 32px; -webkit-overflow-scrolling: touch; } .list-item { scroll-snap-align: start; } |
スクロールコンテナにpadding-bottom: 32px;が定義されていることに注目してください。この目的は、box-shadowが期待通りに表示されるようにスペースを確保することです。
上: paddingなし、下: paddingあり
アバターのリスト
この使用例では、子アイテムのscroll-snap-alignの値としてcenterを使用していることに注目してください。
アバターのリスト
1 2 3 4 5 6 7 8 9 10 |
.list { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; } .list-item { scroll-snap-align: center; } |
centerを定義することで、スクロールコンテナの中央にあることが重要なアバターのリストで役立ちます。
See the Pen
Scroll Snap - Avatars by Ahmad Shadeed (@shadeed)
on CodePen.
高さいっぱいのセクション
スクロールスナップは、垂直スクロールにも役立ちます。例えば、高さいっぱいのセクションです。
高さいっぱいのセクション
1 2 3 4 5 6 7 |
<main> <section class="section section-1"></section> <section class="section section-2"></section> <section class="section section-3"></section> <section class="section section-4"></section> <section class="section section-5"></section> </main> |
1 2 3 4 5 6 7 8 9 10 11 |
main { height: 100vh; overflow-y: auto; scroll-snap-type: y mandatory; -webkit-overflow-scrolling: touch; } .section { height: 100vh; scroll-snap-align: start; } |
See the Pen
Scroll Snap - Full Height Sections by Ahmad Shadeed (@shadeed)
on CodePen.
block値とinline値について
scroll-snap-typeプロパティでは、論理値としてblockとinlineを使用できることは特筆に値します。
1 2 3 |
main { scroll-snap-type: inline mandatory; } |
上記では、英語のような横書きモードではinlineは水平方向を表し、日本語のような縦書きモードではinlineは垂直方向を表します。
アクセシビリティ
スクロールスナップを使用するときは、アクセシビリティを確保してください。
ユーザーがコンテンツを自由にスクロールして読むことができないようにする悪い使用例を紹介します。
1 2 3 4 5 6 7 |
.wrapper { scroll-snap-type: y mandatory; } h2 { scroll-snap-align: start; } |
記事を読むためにスクロールしたら、次の見出しに移動してしまう
実際の動作は、下記のデモでご覧ください。
※こんな実装は絶対にしないでください!
See the Pen
Scroll Snap - Accessibility - Bad Example by Ahmad Shadeed (@shadeed)
on CodePen.
終わりに
コメントや提案があれば、@shadeed9までお願いします。
リソース
- Building a Stories component
- Scroll snapping after layout changes
- Basic concepts of CSS Scroll Snap
- Practical CSS Scroll Snapping
CSSのデバッグに関する電子書籍を書いていることをお知らせします。
興味がある場合は、debuggingcss.comにアクセスして、本に関する最新情報を購読してください。
sponsors