:has(dialog:modal) でモーダル表示中の背面スクロールを止める
html:has(dialog:modal) で overflow: hidden し、scrollbar-gutter: stable でスクロールバー消失時のレイアウトずれを防ぐ。
どんな時に使う?
<dialog> のモーダル(showModal())を開いたとき、背面のページがホイールやタッチでスクロールしてしまうのを止めたいとき。閉じたあとに「いつの間にかスクロール位置が変わっていた」体験を防ぐ。JS で body にクラスを付け外しする定番パターンを、CSS の :has() だけで代替できる。
コード/手順
モーダルな dialog が DOM に開いている間だけ、ルートのスクロールをロックする。あわせて常時 scrollbar-gutter: stable を付け、スクロールバーが消えた瞬間の約15pxの横ずれを防ぐ。
css
html {
scrollbar-gutter: stable;
}
html:has(dialog:modal) {
overflow: hidden;
}:modalはshowModal()で開いた dialog(および同等のモーダル表示)にマッチする。show()だけの非モーダル dialog には効かない。- ロック対象は
html側が分かりやすい。bodyにoverflow: hiddenする流派もあるが、スクロールコンテナがどちらにあるかで合わせて選ぶ。 - スクロール位置の復元は不要(そもそも背面を動かさない)なので、開閉時の
scrollY保存/復元も省略しやすい。
注意点・対応状況
:has()と:modalは主要ブラウザで利用可(2026-10 時点)。古いブラウザ向けには従来どおり開閉時にクラスを付け外しするフォールバックを残す。scrollbar-gutter: stableは「常にガターを確保する」ため、スクロール不要な短いページでも右側に余白が出ることがある。デザイン上気になる場合はモーダル表示中だけ付ける案もあるが、開閉瞬間のガタつきは残る。- ネストしたスクロール可能領域(内側の
overflow: auto)はロックされない。背面の「ページ全体」だけが対象。