はじめに
業務でフロントエンド開発をしている中で、
「iOSのSafariだけUIが崩れる」という問題に遭遇しました。
Chromeでは問題ないのに、iPhoneで見るとレイアウトが崩れる。
最初は「Safariのバグかな?」と思ったのですが、調べていくうちに、
そもそもSafariだけの問題ではなく、vh と viewport のズレが原因
であることに気づきました。
この記事では、特定の解決方法ではなく、
- なぜSafariで崩れて見えるのか?
- viewportとは何か?
- なぜ 100vh でズレが起きるのか?
といった背景理解にフォーカスしてまとめます!
viewportとは?
viewportとは「画面に表示される領域」のことです。
例えば、同じWebページでも、
- PCで見る場合 → 大きな画面いっぱいに表示される
- スマホで見る場合 → 小さい画面に合わせて縮小・再配置される
このとき、「どの範囲を画面として扱うか」を決めているのがviewportです。
なぜ100vhでズレが起きるのか?
height: 100vh;
通常、vh は「viewportの高さの1%」ですが、
この「viewport」の扱いはブラウザによって異なります。
例えば、
- アドレスバーが表示されているとき
- スクロールしてバーが隠れたとき
- 入力キーボードが表示されたとき
といった状態によって、表示領域の高さは変わります。
これはモバイルブラウザとしては自然な挙動です。
Safariが特徴的なのは、vh がこの変化に追従せず、ページ読み込み時の最大の高さで固定されてしまうことです。
そのため、「実際に見えている高さ」と「vhで計算された高さ」がズレるという現象が起きます。
結果として、
- 下に余白ができる
- 要素がはみ出る
- スクロールでズレる
といった現象が起きます。
もっとわかりやすくいうと
例えば、あなたのiphoneの画面の高さが20cmだとしましょう。
初期状態(アドレスバーあり)
- 画面全体:20cm
- アドレスバー:1cm
- 実際に見えている領域:19cm
このとき、ユーザーが見ているのは19cmですが、
100vh は20cmとして計算されることがあります。
キーボードが表示された場合
- キーボード:4cm
- アドレスバー:1cm
- 実際に見える領域:15cm
- ですが、100vhは20cm のまま
この結果、
- フッターが画面の外にいく
- ボタンがキーボードに隠れる
といった問題が発生します。
つまり、
「実際に見えている高さ」と「vhで計算された高さ」がズレること
これがレイアウト崩れの原因です。
なぜ一筋縄で解決しないのか?
100dvh を使って動的にviewportが追従するようにすれば解決するケースもありますが、
実際のアプリでは、
- 他のレイアウトとの兼ね合い
- fixed / flex / scroll の組み合わせ
- JavaScriptによる制御
などが絡み合っているため、
単純に単位を変えるだけでは解決しないことも多いです。
dvh はCSS仕様に正式追加された比較的新しい単位で、svh(UIバー表示時の高さ)・lvh(UIバー非表示時の高さ)・dvh(動的に変化)の3種類があります。
おわりに
同じようにSafariでハマってモヤモヤした人の参考になれば嬉しいです。