スマホで100vhがズレる問題の回避策(svh/lvh/dvhと実装例)
「スマホで100vhを指定したのに、画面からはみ出してスクロールが発生してしまう…」こんな経験はありませんか?PCでは問題なく全画面表示できていたのに、スマホのアドレスバーやツールバーのせいで意図しない余白やズレが生じるのは、多くのコーダーが一度は悩むポイントです。
この記事では、スマホ特有の100vhのズレ問題を根本から解説し、svh・lvh・dvhという新しいビューポート単位を使った実装方法を具体的なコード例とともに紹介します。
この記事で分かること
- スマホで100vhがズレる根本的な原因
- svh・lvh・dvhの違いと使い分け方
- 実際に使える実装例とフォールバック対応
【結論】スマホで100vhがズレる原因と最適解
スマホで100vhがズレる原因は、アドレスバーやツールバーの高さが「vh」の計算に含まれないためです。従来の100vhは、画面全体の高さを基準にしますが、スマホではアドレスバーが表示されている状態とスクロールして隠れた状態で実際の表示領域が変わります。
最適解:新しいビューポート単位「svh(Small Viewport Height)」を使うことで、アドレスバーが表示されている最小の表示領域を基準にできるため、ズレを防げます。
早く知りたい人向け:今すぐ使えるコード
- /* 最もシンプルな対処法(フォールバック込み) */
- .full-screen {
- height: 100vh; /* 古いブラウザ用 */
- height: 100svh; /* スマホのアドレスバー対応 */
- }
スマホで100vhがズレる根本原因を理解する
vhの計算方式とアドレスバーの関係
「vh(Viewport Height)」は、ビューポート(表示領域)の高さの1%を表す単位です。100vhなら「ビューポート全体の高さ」になるはずですが、スマホのブラウザはアドレスバーの表示・非表示で実際の表示領域が動的に変わるため、問題が起こります。
具体的に何が起こるのか
- アドレスバー表示時:画面の一部がアドレスバーに隠れる
- 100vhの基準:多くのブラウザは「アドレスバーが隠れた状態」を基準に計算
- 結果:アドレスバー表示時は100vhが画面からはみ出し、スクロールが発生
注意:この問題はiOS Safari、Android Chromeなど、ほぼすべてのモバイルブラウザで発生します。
新しいビューポート単位:svh・lvh・dvhの違い
CSS Values and Units Module Level 4で、モバイルブラウザのUI変化に対応した新しいビューポート単位が追加されました。
3つの単位の特徴
| 単位 | 正式名称 | 基準 | 適した用途 |
|---|---|---|---|
| svh | Small Viewport Height | アドレスバー表示時の高さ | 確実に画面内に収めたい要素 |
| lvh | Large Viewport Height | アドレスバー非表示時の高さ | 最大表示領域を活用したい場合 |
| dvh | Dynamic Viewport Height | 現在の状態に応じて動的に変化 | スクロールに応じて可変させたい場合 |
どれを使うべき?使い分けの基準
- svh推奨:ファーストビューやヒーローセクションなど、確実に画面内に収めたい要素に最適
- lvh:アドレスバーが隠れることを前提とした最大表示(使用頻度は低い)
- dvh:動的に変化するため、レイアウトが崩れる可能性あり(注意して使用)
実装例:svh/lvh/dvhの実践的なコード
基本的な実装(フォールバック込み)
古いブラウザでは新しい単位が効かないため、必ずフォールバックとして従来のvhも記述します。
- /* ヒーローセクションの実装 */
- .hero {
- height: 100vh; /* フォールバック */
- height: 100svh; /* 新しいブラウザ用 */
- display: flex;
- align-items: center;
- justify-content: center;
- }
min-heightとの組み合わせ
コンテンツ量が多い場合に備えて、min-heightで指定するのがベストプラクティスです。
- /* コンテンツがあふれても対応できる実装 */
- .section {
- min-height: 100vh; /* フォールバック */
- min-height: 100svh; /* 新しいブラウザ用 */
- padding: 20px;
- }
lvh(最大表示領域)の使用例
- /* フルスクリーンモーダルなど、最大領域を使いたい場合 */
- .modal {
- height: 100vh; /* フォールバック */
- height: 100lvh; /* アドレスバー非表示時の最大高さ */
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- }
dvh(動的変化)の使用例と注意点
- /* スクロールに応じて高さが変わる実装 */
- .dynamic-section {
- height: 100vh; /* フォールバック */
- height: 100dvh; /* 動的に変化 */
- }
注意:dvhはスクロール時にレイアウトが再計算されるため、パフォーマンスに影響が出る可能性があります。固定レイアウトにはsvhの方が安全です。
JavaScriptを使った従来の対処法(参考)
新しい単位が使えない場合の代替手段として、JavaScriptで実際の高さを計算する方法もあります。
window.innerHeightを使った実装
- // 実際の表示領域の高さをCSS変数にセット
- function setVh() {
- const vh = window.innerHeight * 0.01;
- document.documentElement.style.setProperty('--vh', `${vh}px`);
- }
- // 初回とリサイズ時に実行
- setVh();
- window.addEventListener('resize', setVh);
- /* CSS側でカスタムプロパティを使用 */
- .hero {
- height: calc(var(--vh, 1vh) * 100); /* --vhがない場合は1vhを使用 */
- }
デメリット:JavaScriptが無効な環境では動作しない、リサイズイベントの負荷がかかるなどの問題があるため、svhが使える環境ではCSS単位を優先すべきです。
ブラウザ対応状況と実装時のチェックポイント
新しいビューポート単位の対応状況
- iOS Safari:15.4以降で対応
- Android Chrome:108以降で対応
- Firefox:101以降で対応
- Edge:108以降で対応
2024年時点で主要モバイルブラウザのほとんどが対応済みですが、古い端末向けにフォールバックは必須です。
実装時のチェックリスト
- ✓ 従来のvh/vwをフォールバックとして先に記述しているか
- ✓ 用途に応じてsvh/lvh/dvhを適切に選んでいるか
- ✓ min-heightとheightを使い分けているか
- ✓ 実機(iOS/Android)で表示確認をしたか
- ✓ アドレスバーの表示・非表示両方の状態でテストしたか
よくある質問
svhとdvhはどちらを使うべきですか?
基本的にはsvh(Small Viewport Height)を推奨します。svhはアドレスバー表示時の高さを基準にするため、確実に画面内に収まります。dvhは動的に変化するため、スクロール時にレイアウトが崩れるリスクやパフォーマンス低下の可能性があります。特別な理由がない限りsvhを使いましょう。
古いiPhoneでも動きますか?
iOS 15.4以降であれば新しいビューポート単位(svh/lvh/dvh)が使えます。それより古い端末では、フォールバックとして記述した従来のvhが適用されます。完全な互換性を保つには、必ず「height: 100vh;」を先に書き、その後に「height: 100svh;」を記述してください。
PCブラウザでも問題なく表示されますか?
はい、問題ありません。PCブラウザではアドレスバーが常に表示されているため、svh/lvh/dvhの違いはほとんど影響しません。デスクトップ環境では従来のvhとほぼ同じ挙動になります。
JavaScriptの対処法と併用できますか?
技術的には可能ですが、推奨しません。CSS単位(svh)とJavaScriptで計算した高さが競合し、予期しない挙動を引き起こす可能性があります。新しいブラウザにはCSS単位を、古いブラウザにはJavaScript対応を、と条件分岐するなら可能ですが、管理が複雑になります。
Bootstrapなどのフレームワークと併用できますか?
はい、問題なく使えます。Bootstrapの「vh-100」クラスなどは従来の100vhを使っているため、カスタムCSSで新しい単位を上書きすればOKです。例:.vh-100 { height: 100svh !important; }のように記述すれば、フレームワークの既存クラスを活かしつつ対応できます。
まとめ
スマホで100vhがズレる問題は、アドレスバーの表示・非表示で実際の表示領域が変わることが原因です。この問題を根本的に解決するには、新しいビューポート単位「svh(Small Viewport Height)」を使うのが最も効果的です。
この記事のポイント
- svh:アドレスバー表示時の高さが基準。確実に画面内に収めたい時に最適
- lvh:アドレスバー非表示時の最大高さ。モーダルなど特殊な用途向け
- dvh:動的に変化。パフォーマンスに注意が必要
- 実装時:必ずフォールバック(従来のvh)を先に記述する
まずは基本の「height: 100vh; height: 100svh;」をヒーローセクションに適用してみましょう。実機で確認すると、アドレスバーが表示されていてもきれいに画面内に収まることが実感できるはずです。




