uga.dev - A Front-end Engineer's shed

現在のテーマは「ライトモード」です。

モーダルダイアログのスクロールロックでレイアウトがガタつく問題

  • #CSS
  • #Web標準
  • #dialog要素
  • #Tips

最近でもないけれど、スクロールロック時に画面幅がガタガタするのをよく見るような気がする、というメモ書き。

body.clientWidth の違いでがんばっていたあの頃

スクロールロックを書いてる時、ロック前のスクロールバーの幅を取得して、ロック後にその値だけbody要素とかにpadding-right当てたくなる。昔は画面幅とか拾って頑張っていました。

なんの話かわからないMacユーザさんは、いったん外観設定から「スクロールバーを表示」の設定で「常に表示」に変更しておきましょう。

スクロールバーがでるくらいのコンテンツ量を持つページでhtml要素にoverflow: hidden;をつけると、コンテンツ幅がガタつきますよね。

そんなお悩みはCSS1行追加で解決

CSSサンプルコード
:root {
  scrollbar-gutter: stable;
}

scrollbar-gutterはスクロールバーが表示される領域(ガター)をあらかじめ確保したい時に便利なプロパティです。

振る舞い

auto

通常通りスクロールバーが必要になったらコンテンツ幅が狭まる。

stable

スクロールバーの有無にかかわらず、スクロールバーが表示される領域を確保しておく。

stable both-edges

スクロールバーの領域を確保しつつ、反対側にも同じ幅だけ余白を生み出す(コンテンツが左右中央に配置される)。

筆者はスクロールバーは「常に表示」にしていますが、その場合はスクロールバーが不要な状態でもスクロールバーの領域が確保されます。

なにもせずにガタガタさせておくよりは、とりあえずstableにするのが良さそうです👀

ついでにつけたいoverscroll-behavior

モーダルダイアログを開いている時、モーダル内のコンテンツをスクロールして最後まで到達すると、背景のbody要素がスクロールしてしまう問題があります。これをscroll chainingと呼びます。

CSSサンプルコード
dialog {
  overscroll-behavior: contain;
}

overscroll-behaviorは、指定するだけでscroll chainingを防ぐことができる優れものです!

振る舞い

auto

スクロールが親要素に伝播する(デフォルト)

contain

スクロールが親要素に伝播しない。

none

スクロールが親要素に伝播しない。macOSやiOSで見られるスクロール端でのバウンドなどの独自動作も無効化される[1]

🍣 実装例

究極、たった3つのルールセットを書くだけでいままで苦労してきたスクロール周りの制御まですっかり綺麗に対応されます[2]

CSSサンプルコード
:root {
  scrollbar-gutter: stable;
}

:root:has(dialog:modal) {
  overflow: hidden;
}

dialog {
  overscroll-behavior: contain;
}

前回の記事でも紹介していたdialog[closedby]もそうですが、いつの間にかダイアログUIまわりの進化がすごいですね。

これ以外にも、いつの間にかできるようになっていることがたくさんあるので、引き続きCSSの進化にも注目していきたいです💃🍣

参考文献

脚注

  • [^1]:-webkit-overflow-scrolling: touchが必要だった時期に-webkit-overflow-scrolling: touchをつけなかった時の動きみたいな感じになる
  • [^2]:CodePen埋め込みをmacOSのSafariで確認するとガターの横幅が一致せずガタつく場合がありそうでした。iframeの中にあるときだけに見えるので同一コンテキストで動作させる場合では問題なさそうです(macOS 26.2)。