iOS Safari における 100vh 問題は、長いあいだフロントエンド開発者を悩ませてきました。
最近はあまり話題に上がらなくなったこともあり、
「もう解決したのでは?」と思われがちですが、実際にはそうではありません。
正確に言うと、問題そのものが解決したわけではなく、回避手段が増えた、という状態です。
この記事では、実際に開発中のサイトで検証した結果をもとに、
svh / dvh / lvhそれぞれの違い- なぜ今回は
100dvhを使わなかったのか - 将来また
dvhを使うとしたら、どんな条件か
このあたりを整理しておきます。
なぜ iOS Safari の 100vh は信用しづらいのか
従来の vh は、レイアウトビューポートを基準に高さが計算されます。
ところが iOS Safari には、
- アドレスバーが表示されている状態
- スクロールするとアドレスバーが隠れる挙動
という特徴があります。
その結果、
- 初期表示では高さが足りない
- スクロールすると要素がはみ出したり、逆に余白が出たりする
といった現象が起きやすくなります。
これが、いわゆる「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としてはあまり好ましくありません。
そのため今回は、
height: 100vh;
height: 100svh;
とし、dvh は外す判断をしました。
将来 dvh を使うとしたら
dvh 自体が悪いわけではありません。
例えば、
- スクロール前提のアプリ風UI
- コンテンツ領域が動的に変わるレイアウト
- 高さ変化がUX上問題にならない画面
こういったケースでは、むしろ dvh のほうが自然に感じることもあります。
要は、
「安定させたいのか」
「現在の表示領域に追従させたいのか」
どちらを優先するか、という判断になります。
まとめ
- iOS Safari の
100vh問題は、完全に解決したわけではない svh / dvh / lvhは 用途に応じて使い分けるための選択肢- メインビジュアルのような領域では、安定性を優先して
svhが無難 dvhは強力だが、レイアウト変化を許容できる場面で使うのが前提
Safari対策は「これ一択」という話ではなく、
画面の役割に応じた割り切りが重要だと感じています。