iOS Safari における 100vh 問題は、長いあいだフロントエンド開発者を悩ませてきました。最近はあまり話題に上がらなくなったこともあり、「もう解決したのでは?」と思われがちですが、実際には問題そのものが解決したわけではなく、回避手段が増えた状態です。
実際に開発中のサイトで検証した結果をもとに、以下の点を整理します。
svh / dvh / lvhそれぞれの違い- なぜ今回は
100dvhを使わなかったのか - 将来また
dvhを使うとしたら、どんな条件か
なぜ iOS Safari の 100vh は信用しづらいのか
従来の vh は、レイアウトビューポートを基準に高さが計算されます。
ところが iOS Safari には、以下の特徴があります。
- アドレスバーが表示されている状態がある
- スクロールするとアドレスバーが隠れる挙動がある
その結果、次のような現象が起きやすくなります。
- 初期表示(アドレスバー表示時)では、
100vhの要素が可視領域より大きくなり、下部が画面外に隠れる - スクロールしてアドレスバーが隠れると見え方が変わり、レイアウトのズレや余白が生じる
これが、いわゆる「Safariの100vh問題」です。
新しい viewport 単位の登場
この問題に対応するため、CSSには以下の viewport 単位が追加されました。それぞれ性質が異なるため、用途に応じた使い分けが求められます。
svh(Small Viewport Height)dvh(Dynamic Viewport Height)lvh(Large Viewport Height)
svh / dvh / lvh の違い
| 単位 | 基準となる高さ | スクロール時の変化 | 特徴 | 向いている用途 |
|---|---|---|---|---|
vh | 従来のビューポート | 変化しない | 互換性は高いが誤差が出やすい | fallback用途 |
svh | 最小のビューポート | 変化しない | 安定する | メインビジュアル、ヒーロー領域 |
dvh | 現在のビューポート | 変化する | 見た目は自然 | アプリ風UI、可変レイアウト |
lvh | 最大のビューポート | 変化しない | 高さが最も大きい | 常時フル表示したい要素 |
dvh は今見えている領域に合わせて動くのが特徴ですが、スクロールに応じて値が変わる点には注意が必要です。
今回試したコード
height: 100vh;
height: 100svh; /* iOS Safari対策 */
height: 100dvh; /* iOS Safari対策 */
min-height: 600px;
この構成は、vh を fallback として残しつつ、対応ブラウザでは svh / dvh で上書きする意図で記述しています。
なぜ 100dvh を使わなかったのか
メインビジュアルに上記の指定を入れて検証したところ、以下の挙動が確認できました。
- アドレスバーが消えた瞬間にメインビジュアルの高さが再計算される
- 画面が動くように見える
見た目としては自然ですが、ファーストビューでレイアウトが動くのはUXとしてあまり好ましくありません。そのため今回は dvh を外し、以下の指定にとどめました。
height: 100vh;
height: 100svh;
将来 dvh を使うとしたら
dvh 自体が悪いわけではありません。例えば、以下のようなケースでは dvh のほうが自然に感じられることもあります。
- スクロール前提のアプリ風UI
- コンテンツ領域が動的に変わるレイアウト
- 高さ変化がUX上問題にならない画面
判断の基準は、以下のどちらを優先するかにあります。
「安定させたいのか」
「現在の表示領域に追従させたいのか」
まとめ
- iOS Safari の
100vh問題は、完全に解決したわけではない svh / dvh / lvhは用途に応じて使い分けるための選択肢- メインビジュアルのような領域では、安定性を優先して
svhを選ぶのが無難 dvhはレイアウト変化を許容できる場面で使うのが前提となる
Safari対策は特定の単位一択で決まるものではなく、画面の役割に応じた割り切りが求められます。