出典元: CSS Scroll Snap - Ahmad Shadeed
アプリなどで横スクロールしてコンテンツにスナップするといった動作をよく見かけると思いますが、CSSで横スクロールのUIを作成しようとすると、専用のjavascriptやライブラリを使用する必要があったりと実装が大変です。
しかし、CSSスクロールスナップは横スクロールしてスナップするといった動作を行うことができます。
この記事では、CSSスクロールスナップ(scroll-snap)の基本について説明します。
scroll-snapを使用する理由
スマホやタブレットの普及で、タッチでスワイプできる画面を設計、構築する必要があるかと思います。
たとえば、ギャラリー風のデザインを考えてみましょう。ユーザーは、階層構造ではなく、左または右に簡単にスワイプして、より多くの画像を表示できます。
CSSの仕様によると、CSSスクロールスナップを導入することでユーザーエクスペリエンスが向上し、スクロールの実装が容易になり、ユーザーと開発者、双方にメリットがあるということです。
スクロールコンテナーの基本
スクロールコンテナーを作成するために必要な基本的なものは次のとおりです。
- overflowをvisible以外の値で使用
- 項目を隣同士(インライン)に表示す
例を見てみましょう。
<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>
.section {
white-space: nowrap;
overflow-x: auto;
}
以前まではwhite-space: nowrap
を使用することで要素を横並びで表示するようにしていました。
近年では、この方法の代わりにFlexbox
を使用することで表現可能です。
.section {
display: flex;
overflow-x: auto;
}
これは、スクロールコンテナーを作成するための基本的な構成です。
しかし、これだけではスクロールコンテナーの機能としては不十分です。
従来の問題
従来の問題は、スワイプの仕組みと比較して、優れたUXが提供されないことです。タッチスクリーンでのスワイプジェスチャの主な利点は、1本の指で水平または垂直にスクロールできることです。
以前のソリューションでは、ただスクロールするだけです。文字通り、各アイテムをそれぞれの場所に移動する必要があります。これはスワイプではなく、ユーザーにとって非常にストレスを与える結果になってしまします。
CSSスクロールスナップを使用すると、ユーザーが水平方向または垂直方向にスクロールしやすくなるスナップポイントを定義するだけで、この問題を解決できます。
CSSスクロールスナップの使用方法を見てみましょう。
CSSスクロールスナップの紹介
コンテナーでスクロールスナップを使用するには、その子アイテムをinlineで表示する必要があります。これは、上記で説明した方法の1つで実行できます。 CSSフレックスボックスを使用します。
<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>
.section {
display: flex;
overflow-x: auto;
}
さらに、スクロールスナップを機能させるために2つのプロパティを追加します。
まず、スクロールコンテナーにscroll-snap-type
を追加します。この例では、これは.section
要素です。
次に、子アイテム(.section__item
)にscroll-snap-align
を追加します。
.section {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.section__item {
scroll-snap-align: start;
}
x mandatory
とstart
の値についてはじめて見たと思われるかもしれませんが、この部分が今回の記事のメインになる部分です。
これらのプロパティにより、スクロールコンテナーの開始位置にスナップすることが可能になって、スクロールがより自然になりました。
それでは、スクロールスナップのプロパティについて詳しく見ていきましょう。
scroll-snap-type
CSSの仕様 によると、scroll-snap-type
は、ある要素がスクロールスナップコンテナーであるかどうか、どの程度厳密にスナップするか、どの軸を考慮するかを指定するものです。
それを解析してみましょう。
スクロールスナップコンテナーの軸について
スクロールスナップコンテナーの軸は、スクロールの方向を表します。水平または垂直にできます。
xの値は水平方向のスクロールを、yの値は垂直方向のスクロールを表します。
/* Horizontal */
.section {
display: flex;
overflow-x: auto;
scroll-snap-type: x;
}
/* Vertical */
.section {
height: 250px;
overflow-y: auto;
scroll-snap-type: y;
}
スクロールスナップコンテナーの精密さ
スクロールスナップの方向だけでなく、その精密さも定義できます。
これは、scroll-snap-type
値にmandatory | proximity
のいずれかの値を使用することで可能です。
mandatory
は、ブラウザが各スクロールポイントにスナップする必要があることを意味します。scroll-snap-align
プロパティの値がstart
であると仮定してみましょう。つまり、スクロールはスクロールコンテナーの開始点にスナップする必要があります。
下図では、ユーザーが右方向にスクロールするたび、ブラウザはコンテナーの開始点にアイテムをスナップさせます。
.section {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
}
.section__item {
scroll-snap-align: start;
}
下のデモで右方向にスクロールしてみてください。スクロールバーを右に移動させるか、スマートフォンやタブレットの場合はタッチ操作で行ってください。各アイテムがコンテナーの始まりにスナップする様子が感じられるはずです。
しかし、値がproximity
の場合は、ブラウザが作業を行います。定義された点(この例ではstart
)にスナップするかもしれません。proximity
はデフォルトの値ですが、わかりやすくするために追加しておきます。
.section {
display: flex;
overflow-x: auto;
/* proximityはデフォルト値です。 */
scroll-snap-type: x proximity;
}
スクロールスナップの方向
スクロールコンテナーの子アイテムには、スナップできるアライメントポイントが必要です。
start
、center
、end
のいずれかを使用します。
スクロールコンテナーに磁石があり、スナップポイントを制御するのに役立つと想像してみてください。
scroll-snap-type
が垂直の場合、スナップの配置は垂直になります。次の図を参照してください。
これをより明確にするため、 start
、center
、およびend
の以下のアニメーションを参照してください。
スクロールコンテナーの start
子アイテムは、水平スクロールコンテナーの先頭にスナップします。
スクロールコンテナーの center
子アイテムは、スクロールコンテナーの中央にスナップします。
スクロールコンテナーの end
子アイテムは、スクロールコンテナーの最後にスナップします。
scroll-snap-stopを使う
ユーザーがあまりに速くスクロールするなど、スクロール中にユーザーが誤って重要な項目をスキップしてしまうのを防ぐ方法が必要な場合もあります。
.section__item {
scroll-snap-align: start;
scroll-snap-stop: normal;
}
スクロールのスピードが速すぎる(勢いよくスクロールする)と、3つも4つも項目が飛ばされることもあります。
scroll-snap-stop
のデフォルト値はnormal
です。スクロールを強制的にすべての可能なポイントにスナップするには、always
を使用する必要があります。 scroll-snap-stop: always
を使用すると、ブラウザは各スナップポイントで停止します。
.section__item {
scroll-snap-align: start;
scroll-snap-stop: always;
}
そうすれば、ユーザーは1つずつスナップポイントをスクロールしていくことができ、重要な項目をスキップすることを避けることができます。
各ストップポイントにストップサインがあることを想像してください。
デモで下のスクロールを試して、オプションを切り替えてみてください。
スクロールスナップのpadding
scroll-padding
短縮形プロパティは、padding
プロパティの動作と同様に、すべての側面にスクロールパディングを設定します。
下図では、スクロールコンテナーの左側に50pxのパディングが設定されています。その結果、子要素は左端から50pxずれた位置にスナップします。
.section {
overflow-x: auto;
scroll-snap-type: x mandatory;
scroll-padding: 0 0 0 50px;
}
同じことが垂直スクロールでも機能します。以下の例を参照してください。
.section {
overflow-y: auto;
scroll-snap-type: y mandatory;
scroll-padding: 50px 0 0 0;
}
スクロールスナップのmargin
scroll-margin
ショートハンドプロパティは、スクロールコンテナーの子アイテム間の間隔を設定します。要素にマージンが追加されると、マージンに応じてスクロールがスナップします。下図を参照してください。
.item-2
はscroll-margin-left: 20px
となっています。その結果、スクロールコンテナーはそのアイテムの手前20pxにスナップします。ユーザーが再び右にスクロールしたとき、.item-3
はスクロールコンテナーの開始位置にスナップすることに注意してください。つまり、マージンを持つ要素のみが影響を受けることになります。
CSSスクロールスナップの使用例
画像リスト
CSSスクロールスナップの優れた使用例として、画像のリストが挙げられます。スクロールスナップを使用することで、より良いスクロール体験を提供します。
.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
になります。
友達リスト
スクロールスナップのもう1つの優れた使用例として、友達のリストがあります。以下の例は、Facebookから引用したものです(実例)。
.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です。この目的は、ボックスシャドウが期待どおりに表示されるように余分なスペースを提供することです。
アバターリスト
今回の使用例では、子アイテムのscroll-snap-align
の値としてcenter
を使用しています。
.list {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
}
.list-item {
scroll-snap-align: center;
}
これはアバターのリストで、アバターがスクロールするコンテナーの中央にあることが重要な場合に便利です。
全画面を覆うような要素
スクロールスナップの使用は、縦スクロールの場合にも有効です。この例として、フルハイトのセクションがあります。
<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>
main {
height: 100vh;
overflow-y: auto;
scroll-snap-type: y mandatory;
-webkit-overflow-scrolling: touch;
}
.section {
height: 100vh;
scroll-snap-align: start;
}
ブロックおよびインラインの値
特筆すべきは、scroll-snap-type
にinline
とblock
という論理値を使用できることです。以下の例を参照してください。
main {
scroll-snap-type: inline mandatory;
}
この例では、英語のような横書きモードでは、inline
が横方向の寸法を表します。日本語のような言語では、inline
は縦方向の寸法を表すことになります。
CSSの論理プロパティについてもっと知りたい方は、Adrian Roselliの記事 をご覧ください。
アクセシビリティ
CSSスクロールスナップを使用する際は、アクセシビリティを確保すること。ここでは、ユーザーがコンテンツを自由にスクロールして読むことを妨げる、スクロールスナップの悪い使い方を紹介します。
.wrapper {
scroll-snap-type: y mandatory;
}
h2 {
scroll-snap-align: start;
}
デモのような実装は絶対にしないでください。
まとめ
以上がscroll-snap
機能についてになります。今までスナップするスクロールを実装する場合はjsを使用していましたが、cssのみで実装可能となると簡単になりますね。