オーバースクロールの挙動
問題
ネストされたスクロール可能領域 — モーダル、サイドバー、ドロップダウン、チャットパネル — の端までスクロールすると、ブラウザはスクロールイベントを最も近いスクロール可能な祖先に「チェーン」します。背景のページがオーバーレイの下で突然スクロールし始めます。これはスクロールチェーンと呼ばれ、ウェブアプリケーションで最もよく見られるUXバグの1つです。
overscroll-behavior が登場する前は、これを防ぐためにJavaScriptのスクロールロックハックが必要でした:wheelイベントのリスニング、スクロール位置の計算、適切なタイミングでの preventDefault() 呼び出しです。これらのソリューションは脆弱で、カクつきを引き起こし、タッチデバイスのスクロールを完全に壊すことが多かったです。モバイルでは、プルトゥリフレッシュのようなオーバースクロールエフェクトがカスタムスクロール領域内で予期せずトリガーされることもありました。
解決方法
overscroll-behavior プロパティは、スクロールコンテナが境界に達した時の動作を1行のCSSで制御します。
auto(デフォルト):通常の動作 — 親へのスクロールチェーンとネイティブオーバースクロールエフェクト(バウンス、プルトゥリフレッシュ)が有効です。contain:親へのスクロールチェーンを防止しますが、ネイティブオーバースクロールエフェクト(iOS/macOSのラバーバンドバウンスなど)は要素自体の中では依然として適用されます。none:スクロールチェーンとすべてのネイティブオーバースクロールエフェクトの両方を防止します。スクロールは単に停止します。
overscroll-behavior-x と overscroll-behavior-y で各軸を個別に制御することもできます。
基本原則
auto(デフォルト)
デフォルト値です。要素がスクロール境界に達すると親にチェーンします。メインページコンテンツでは通常問題ありませんが、オーバーレイ、モーダル、サイドバーでは問題を引き起こします。
contain
最もよく必要とされる値です。スクロールチェーンを防止し、スクロールが要素内に閉じ込められます。モーダル、サイドバー、チャットパネル、ドロップダウンメニューなど、背景をスクロールさせたくない独立したスクロール可能領域に使用しましょう。
none
contain よりさらに進んで、ラバーバンドバウンスやプルトゥリフレッシュなどのネイティブオーバースクロールエフェクトも抑制します。境界でスクロールが視覚的フィードバックなしで完全に停止してほしい場合に使用します。埋め込みアプリライクなインターフェースに便利です。
コード例
モーダルでのスクロールチェーン防止
.modal-body {
max-height: 80vh;
overflow-y: auto;
overscroll-behavior-y: contain;
}
この1行で、モーダルコンテンツの端までスクロールした際に背景ページがスクロールするのを防止します。
ブラウザの戻るナビゲーションの誤発動防止
.horizontal-scroller {
overflow-x: auto;
overscroll-behavior-x: contain;
}
一部のブラウザでは、水平オーバースクロールジェスチャーがブラウザの戻る/進むナビゲーションをトリガーします。水平スクロール領域に overscroll-behavior-x: contain を設定することで、この誤ナビゲーションを防止します。
コンテナを制御したアプリシェル
.app-shell {
display: grid;
grid-template-columns: 250px 1fr 300px;
height: 100vh;
}
.sidebar-nav {
overflow-y: auto;
overscroll-behavior: contain;
}
.main-content {
overflow-y: auto;
}
.detail-panel {
overflow-y: auto;
overscroll-behavior: contain;
}
マルチパネルレイアウトでは、各セカンダリスクロールパネルに overscroll-behavior: contain を適用し、メインコンテンツ領域へのスクロールチェーンを防止しましょう。
プルトゥリフレッシュの無効化
body {
overscroll-behavior-y: none;
}
モバイルブラウザでは、ページ上部で下に引くとリフレッシュがトリガーされます。body に overscroll-behavior-y: none を設定することでこの動作を無効にできます。独自のリフレッシュメカニズムを持つウェブアプリに便利です。
AIがよくやるミス
overscroll-behaviorを一切提案しない:単一のCSSプロパティで解決できる問題にJavaScriptのスクロールロックライブラリやevent.preventDefault()ハックをデフォルトで使用します。- 間違った要素に適用する:
overscroll-behaviorはスクロールを持つ要素(overflow: autoまたはoverflow: scroll)に設定する必要がありますが、親やラッパーに設定してしまいます。 containではなく常にnoneを使用する:noneはすべてのオーバースクロールフィードバックを抑制するため、不自然に感じることがあります。バウンスエフェクトを特に抑制する必要がない限り、containを優先しましょう。- 軸固有のバリアントを忘れる:水平スクローラーの
overscroll-behavior-x: containのように1軸のみ制御が必要な場合にoverscroll-behavior: containを使用します。 overflowと組み合わせない:overscroll-behaviorはスクロールコンテナにのみ効果を発揮します。要素にoverflow: autoまたはoverflow: scrollがない場合、このプロパティは効果がありません。
使い分け
- モーダルとダイアログ:モーダル内でスクロールした際の背景ページスクロールを防止します。
- サイドバーとナビゲーションパネル:サイドバーのスクロールをメインコンテンツから独立させます。
- チャット・メッセージングパネル:ユーザーがメッセージ履歴をスクロールする際のページスクロールを防止します。
- ドロップダウンメニュー:長いドロップダウンが背後のページをスクロールさせないようにします。
- 水平スクロール領域:
overscroll-behavior-x: containでブラウザの戻る/進むナビゲーションの誤発動を防止します。 - モバイルウェブアプリ:アプリが独自のリフレッシュロジックを処理する場合、body に
overscroll-behavior-y: noneでプルトゥリフレッシュを無効にします。