Safariの100vh問題は「解決」していない。ただし、選択肢は増えた

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対策は「これ一択」という話ではなく、
画面の役割に応じた割り切りが重要だと感じています。