CSSの100vhがスマホでずれるのをdvh・svh・lvhで直す方法
スマートフォンで height: 100vh にした要素の下の端が、アドレスバーの分だけ画面の外にはみ出すのは、vh が「アドレスバーが引っ込んだときの、大きいほうの画面の高さ」を基準にしているためです。トップページのファーストビュー(ヒーロー)や全画面のメニューで、下の端に置いたボタンが隠れる、という形で現れます。100vh を 100svh(小さいほうの高さ)か 100dvh(そのときの高さ)に書き換えると、アドレスバーが出ていても画面に収まる、と仕様では説明されています。
検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、ヘッドレスの Chromium 153 です。検証に使ったヘッドレスの Chromium には、スクロールに合わせて出たり引っ込んだりするアドレスバーがありません。そのため、アドレスバーによって svh と lvh の値が変わる様子は確かめていません。その部分は CSS の仕様と MDN の説明をもとに書いています。Chromium で確かめたのは、単位の値、古いブラウザ向けの書き方、min-height との組み合わせです。iPhone や Android の実機、Safari、Firefox は試していません。
100vh は、アドレスバーが引っ込んだときの高さ
CSS Values and Units Module Level 4 の仕様では、ビューポート(ブラウザでページを表示する領域)の大きさを3通りに分けています。スマートフォンのブラウザでは、ページをスクロールし始めると、アドレスバーの一部または全部を隠すことがよくある、という例も仕様に書かれています。
| 単位 | 基準にする高さ | 値が変わるとき |
|---|---|---|
lvh |
アドレスバーなどが引っ込んだときの高さ(大きいほう) | 画面そのものの大きさが変わったときだけ |
svh |
アドレスバーなどが出ているときの高さ(小さいほう) | 画面そのものの大きさが変わったときだけ |
dvh |
そのときの高さ | アドレスバーが出たり引っ込んだりしたときも変わる |
vh |
lvh と同じ |
lvh と同じ |
いつもの vh は、lvh と同じ大きいほうの高さで決まります。MDN の長さの単位のページにも、vh は lvh と同等だと書かれています。仕様には、Safari にならって多くのブラウザが大きいほうに合わせたこと、いまではウェブの互換性のために lvh に合わせることが必要と考えられていることが書かれています。
このため、アドレスバーなどが出ているあいだは、100vh の要素の下の部分が、その分だけ隠れることがあります。仕様の説明をもとに、3つの単位で箱の高さを指定したときの見え方を図にしました。

ファーストビューは min-height: 100svh にする
トップページのファーストビューのように、「最初に開いたときに、1画面にちょうど収めたい」要素には svh が向いています。仕様では、svh の大きさは、アドレスバーなどがすべて出ているときに、中身が隠れずに画面をちょうど満たすと説明されています。アドレスバーが引っ込むと下にすき間ができますが、値はスクロール中に変わりません。仕様はこの性質を、一般に、より「安全」な単位だと表現しています。
.hero {
min-height: 100vh; /* svh を使えないブラウザ向け */
min-height: 100svh;
}
height ではなく min-height にしておくと、中身が画面より多いときに要素が伸びます。画面を 400×700px にして、高さ 900px の中身を入れて測ったところ、min-height: 100svh の要素は 900px に伸びました。height: 100svh の要素は 700px のままで、中身が下にはみ出しました(scrollHeight は 900px)。
全画面のメニューや下にボタンがある画面は 100dvh にする
ハンバーガーメニューを開いたときの全画面のパネルや、チャットの入力欄のように画面の下の端に置きたいものには、dvh が向いています。仕様では、dvh を使うと、アドレスバーなどがあってもなくても、中身がビューポートにちょうど収まると説明されています。なお、仕様では、画面に出るキーボードは、ふつうは中身の上に重なって表示され、どのビューポートの単位にも影響しないものの例に挙げられています。
.menu-panel {
position: fixed;
inset-inline: 0;
top: 0;
height: 100vh; /* dvh を使えないブラウザ向け */
height: 100dvh;
}
ただし、dvh の値は、画面の大きさが変わらなくても変わります。仕様では、スクロールしている最中に中身の大きさが変わることがあり、使い方によっては、読む人の邪魔になったり、処理の負担が増えたりすると注意しています。また、アドレスバーが動いているあいだ、ブラウザは dvh の値を少しずつ変える必要はなく、出きったときか引っ込みきったときの値で計算してよい、とも書かれています。
ページと一緒にスクロールする要素の高さを dvh で決めると、アドレスバーが出たり引っ込んだりするたびに、その要素の高さが変わります。スクロール中に大きさが変わってほしくない要素は、svh か lvh にします。
隠れてもかまわない背景は 100lvh(100vh)のままでよい
全面に敷く背景の画像や色のように、下の端が少し隠れても困らず、アドレスバーが引っ込んだときにすき間が出るほうが困るものは、lvh(vh でも同じ)のままにします。仕様の説明でも、lvh は、アドレスバーなどが引っ込んだときにビューポートを必ず満たすが、出ているときには隠れる部分がある単位とされています。
いまの 100vh を書き換えるなら、まず 100svh に置き換えます。svh はスクロール中に大きさが変わりません。
古いブラウザ向けに 100vh を先に書く
MDN の互換性データ(@mdn/browser-compat-data 8.1.9)では、svh・lvh・dvh は、Chrome(Android 版も)108、Firefox 101、Safari(iOS 版も)15.4 から使えます。
これらのバージョンより古いブラウザにも対応するなら、上のコード例のように 100vh の行を先に書き、その下に新しい単位の行を書きます。CSS Syntax Module Level 3 の仕様では、構文に合わない値の宣言は無効になり、無視されると定めています。そのため、古いブラウザでは 100vh の行が残ります。基本の書き方は「CSSで、要素を画面の真ん中に置く方法」の記事でも確かめています。
検証環境の Chromium は dvh に対応しているので、新しい単位を知らない古いブラウザの代わりに、存在しない単位 qvh を書いて確かめました。どの宣言が効いたか分かるように、新しい単位の側は 50 にしています。画面の高さは 700px です。
| 書いた CSS | 要素の高さ |
|---|---|
height: 100vh; height: 50dvh; |
350px(下の行が効いた) |
height: 100vh; height: 50qvh; |
700px(下の行が無視され、100vh が残った) |
height: 50dvh; height: 100vh; |
700px(順番が逆だと、dvh に対応していても 100vh になる) |
順番を逆にすると、新しいブラウザでも 100vh が使われてしまいます。新しい単位の行は必ず後に書きます。
1行ずつ並べる代わりに、@supports で分けることもできます。検証環境では CSS.supports('height', '100dvh') が true になりました。次の CSS の 100dvh を 50dvh に変えて試したところ、高さは 350px(@supports の中の側)になりました。
.panel {
height: 100vh;
}
@supports (height: 100dvh) {
.panel {
height: 100dvh;
}
}
検証環境のヘッドレス Chromium では、4つの単位がどれも同じ値になる
検証環境のヘッドレス Chromium では、アドレスバーが出たり引っ込んだりしません。Playwright でスマートフォンの画面をまねて、100vh・100svh・100lvh・100dvh の要素の高さを測ったところ、次のようになりました。
| まねた端末 | 画面(screen)の高さ | 表示領域の高さ | 4つの単位の高さ |
|---|---|---|---|
Pixel 7 |
915px | 839px | どれも 839px |
iPhone 15 |
852px | 659px | どれも 659px |
Playwright の端末の設定では、表示領域の高さが、画面の高さより小さい決まった値になっています。アドレスバーが動くことはないので、svh と lvh も同じ値でした。表示領域(ビューポート)の高さを 700px から 640px に変えたときは、4つの単位がそろって 700px から 640px に変わりました。
そのため、アドレスバーとの重なりが直ったかどうかは、スマートフォンの実機で、ページを開いた直後(アドレスバーが出ている状態)と、少しスクロールしたあと(引っ込んだ状態)の両方を見て確かめます。
下の端のボタンが、iPhone のホームバーに重なる場合の対処は、「CSSで、iPhoneのノッチやホームバーに隠れないようにする方法」の記事で確かめています。





