メインコンテンツへスキップ
ブログ一覧に戻る
Web制作・運用

「100vhがはみ出す」はバグではない|svh・lvh・dvh が分けているのは精度ではなく代償

2026年10月21日
12分で読めます

この記事でわかること

スマートフォンで height 100vh がはみ出す理由を仕様から確認したい方へ。仕様は large / small / dynamic の3つのビューポートを定義し、既定の vh は「画面上の部品が引っ込んだ状態」を基準にしています。はみ出しは定義どおりの挙動です。隠れて困るものとリサイズされて困るものから単位を選ぶ判断軸を整理します。

スマートフォンで height: 100vh を指定した要素が、画面よりわずかに大きくなる。下端のボタンがアドレスバーの裏に隠れる。これに出会ったとき、最初はブラウザ側の気まぐれだと考えていました。端末やブラウザによって挙動が違うように見えるからです。

気まぐれとして扱うと、対処も気まぐれになります。JavaScript で実際の高さを測って CSS 変数に書き戻す、という補正コードが足されていきます。

しかし仕様を読み直すと、はみ出しは定義どおりの挙動でした。既定のビューポート百分率単位は「画面に見えている高さ」を指していません。本記事が立てる仮説はこれです——svh / lvh / dvh の3つは精度の違いではなく、どの代償を払うかの選択であり、「新しいほうを使う」は判断になっていないのではないか。

この見立てが当たっているなら、起きる失敗は具体的です。最新の dvh に一括置換して「はみ出しは直った」と判断する。そのあとスクロール中にレイアウトが伸び縮みする別の症状が出て、原因が単位の変更にあると気づかないまま、また別の対処が足される。

本記事は単位の選び方に絞ります。モバイル表示全体の最適化手順はモバイルパフォーマンス最適化完全ガイドに預け、ここでは扱いません。ブラウザごとの実装状況も扱いません(今回確認した情報源に記載がないためです)。

30秒で要点

  • 仕様は large / small / dynamic の3つのビューポートサイズを定義し、既定の v* 単位と lv* 単位は動的なUI(アドレスバーなど、操作に応じて出入りする画面上の部品)が引っ込んだ状態を想定したサイズを基準にしている
  • したがって、UIが展開されているあいだ内容が隠れうることは、仕様が明記している性質である
  • sv* は内容が隠れない代わりに、UIが引っ込むと要素の周りに余分な空間が出る
  • dv* は large と small のどちらの代償も避けようとする代わりに、ビューポート自体が変わらないあいだでも値が安定せず、スクロール中にコンテンツがリサイズされうる
  • 3つは優劣ではなく代償の種類の違い。選ぶ基準は「隠れて困るものは何か」「リサイズされて困るものは何か」の2問

仕様が定義しているのは「3種類の大きさ」である

参照するのは W3C の CSS Values and Units Module Level 4 の §6.1.2.1 です(2026年10月21日に取得して確認)。

先に断っておきます。この文書は作業草案(Working Draft)です。「Status of this document」節が、この文書は草案であり、いつでも更新・置換・廃止されうる、作業中の文書以外として引用するのは不適切である、と自ら述べています。本記事も、これを確定した勧告としては扱いません。読める範囲は「2026年10月21日に確認した時点で、この草案にこう書かれている」までです。

そのうえで、書かれている定義は明確です。

表が収まらない場合は、横にスクロールできます

単位基準とするビューポート仕様が述べている性質
lv*(および既定の v*)動的に出入りするUIが引っ込んだ状態を想定したサイズUIが展開されたとき、内容がその裏に隠れうる
sv*動的に出入りするUIが展開された状態を想定したサイズ内容は隠れない。UIが引っ込むと要素の周りに余分な空間が出る
dv*UIの出入りに追従して変わるサイズビューポート自体が変わらないあいだでも値が安定しない

ここで注目したいのは、lv* の定義に既定の v* が併記されている点です。vh は「見えている高さの1%」ではなく、「UIが引っ込んだと仮定したときの高さの1%」として定義されています。

つまり、アドレスバーが出ている状態で height: 100vh の要素が画面からはみ出すのは、仕様が想定している結果です。仕様は large viewport の説明に続けて、UIが展開されたときに内容がその裏に隠れうると書いています。

安定するものと、安定しないもの

もう1つ、設計上重要な区別が定義に含まれています。

large と small の単位には、それぞれ「ビューポート自体がリサイズされない限り、大きさは固定であり、したがって安定している」という性質が明記されています。一方 dynamic の単位には、「ビューポート自体が変わっていないあいだでも大きさは安定しない」と書かれています。

仕様は dynamic 系の注意点をさらに具体的に述べています。これらの単位を使うと、たとえばユーザーがページをスクロールしているあいだにコンテンツがリサイズされうる。使い方によっては、それはユーザーにとって不快なものになりうるし、パフォーマンスの面でコストにもなりうる、と。

同じ趣旨は MDN の <length> のリファレンスにも注記として置かれています(2026年10月21日確認。ページ末尾の最終更新表示は2026年7月8日)。こちらは「ユーザーインターフェースの劣化を招き、パフォーマンスへの打撃を引き起こしうる」という表現になっています。

ここで単位の性質が、ようやく並べられる形になります。dvh が払う代償は「値が安定しないこと」です。隠れることも余白が出ることも避けようとする対価として、合わせ続ける動きが入ります。

「vh は lvh と同じ」は、どこまで前提にできるか

実装の話として、既定の vh は lvh と同じ値になる、と説明されることがあります。MDN もそう書いています。ただし書き方に条件が付いています。

MDN は、既定のビューポートサイズはブラウザが定義するものであり、その結果として得られる単位の挙動は、small に基づくもの・large に基づくもの・その中間・dynamic に基づくもののいずれであってもよい、と述べています。そのうえで「現時点では、すべての既定のビューポート単位(vh、vw など)は large の対応物(lvh、lvw など)と等価である」と書いています。

この「現時点では」は読み飛ばしやすい箇所です。書かれているのは観測された現状であって、仕様が与えた保証ではありません。

ここは前提の種類を分けておく価値があります。

  • 仕様が定義していること:lv* と既定の v* が large viewport を基準とすること(草案の記述として)
  • MDN が現状として記述していること:すべての既定単位が large の対応物と等価であること
  • どちらも保証していないこと:この等価関係が将来も続くこと

実務への影響は小さくありません。「vh でも lvh でも同じだから書き分けない」という判断は、現状の記述に依存した判断です。意図として large を使いたいのなら、lvh と書いておくほうが、意図と記述が一致します。これは互換性の話ではなく、何を意図したかをコードに残すかという話です。

もう1点、仕様は実装側の自由度についても書いています。あるUIの展開・収縮が、すべてのビューポート百分率単位を同時に変化させるのか、それとも large と small の差に寄与するのかは、おおむねUA(ブラウザ)依存である、と。ただし例外が置かれています。スクロールなどの頻繁な操作の結果として起きる変化で、大きなレイアウト変更になればユーザーを妨げるようなものは、後者(large と small の差に寄与する側)に分類しなければならないと要件になっています。仕様が実装に課している要件であり、各ブラウザがこれを満たしているかの確認は本記事では行っていません。

なぜ「最新の単位を使う」が判断に見えてしまうのか

ここまでの整理をしても、現場では dvh が選ばれやすい状況があります。理由は単位の並び方にあります。

vh の隣に svh / lvh / dvh という名前の付いた変種が並んでいると、既定のものより新しく精密な選択肢が後ろに控えているように読めます。そして実際に dvh は「はみ出す」という最初の症状を解決します。症状が消えるので、解決したという感触が残ります。

見落とされるのは、代償が別の時間軸に現れることです。はみ出しは最初の描画で見えます。スクロール中のリサイズは、操作しているあいだに現れます。確認が「開いてスクショを撮る」で終わっていると、後者は観測されません。症状の消えた瞬間だけを見ていると、取引をしたことに気づけません。

同じ構造は svh にもあります。svh を選ぶと内容は隠れませんが、UIが引っ込んだときに余白が出ます。仕様とMDNはこれを踏まえて、small の単位は一般にはより「安全」だが、ユーザーがページを操作し始めたあとに最も見栄えのよいレイアウトにはならないかもしれないと書いています。この「安全」は、仕様・MDNの双方が引用符付きで使っている語です。無条件の推奨ではありません。

判断軸:2つの問いに還元する

3つの単位から選ぶとき、立てる問いは2つです。

問い1:この要素で、隠れて困るものは何か。

画面の下端に置いた確定ボタン、同意のチェックボックス、モーダルの閉じるボタン。これらが一時的にでもUIの裏に入るなら、lvh(および既定の vh)は選べません。svh が候補になります。隠れるのが装飾的な余白や背景なら、lvh のままで足ります。

問い2:この要素で、リサイズされて困るものは何か。

スクロールに追従する要素、途中で高さが変わると読んでいる位置が飛ぶ長文、再レイアウトが重いリストやグリッド。これらが入っているなら、dvh は代償が大きい側になります。逆に、一度表示したら操作中に高さが変わらない領域(起動直後のスプラッシュ的なファーストビューなど)であれば、dvh の代償は小さくなります。

2問の答えが衝突する場合——隠れても困るしリサイズされても困る——は、単位の選択で解く問題ではありません。レイアウトの前提を変える段階です。画面いっぱいの高さを使わない、固定配置を外す、内容を分割する、といった判断に戻ります。

「直った」の定義を先に決める

単位を入れ替える作業には、測るものを決めてから入ります。そうしないと、代償を払ったかどうかが記録に残りません。

  • 何を測るか:対象要素の下端が表示領域の下端をどれだけ超えているか
  • 単位:ピクセル(CSSピクセル)
  • 測る状態:UIが展開された状態と、引っ込んだ状態の両方
  • 満たすべき条件:両方の状態で、隠れて困るもの(問い1で挙げたもの)が表示領域の内側にあること

この定義を置くと、片方の状態だけを見て「直った」と言えなくなります。そして dvh を選んだ場合には、もう1つ観測項目が増えます。スクロール中に対象要素の高さが変化するかです。変化してよいなら dvh の代償を受け入れたということ、変化して困るなら選び直すということになります。

手元で試せる最小の確認

実機を1台用意して、確認を1回だけ行います。

  1. 確認したい画面を開き、アドレスバーなどのUIが展開された状態で、下端に置いた要素が表示領域の内側にあるかを見る
  2. 少しスクロールしてUIが引っ込んだ状態にし、同じ要素と、その周囲の余白を見る
  3. そのままスクロールを続けながら、要素の高さが変わるかを見る

この3手で、3つの単位の代償が1つずつ観測できます。1ではみ出すなら large 系の代償、2で余白が出るなら small 系の代償、3で高さが動くなら dynamic 系の代償です。どれも「不具合」ではなく、選んだ単位が約束している挙動です。

観測しておくべきは、どれが起きたかではなく、起きたことが自分の画面で許容できるかです。

確かなことと、確かでないこと

確かなこと(2026年10月21日に情報源を取得して確認した範囲):3つのビューポートサイズの定義、lv* と既定の v* が large を基準とすること、large/small の値が安定し dynamic の値が安定しないこと、dynamic でスクロール中のリサイズが起こりうることとその不快さ・コストについての記述、small が一般には「安全」だが操作開始後に最も見栄えのよいレイアウトになるとは限らないという記述、既定単位がどのサイズに対応するかはブラウザが決めるという記述。

確かでないこと:参照した W3C 文書は作業草案であり、仕様自身が更新・置換・廃止されうると述べています。MDN の「現時点では既定単位は large と等価」は現状の記述であり、将来の保証ではありません。

本記事が踏み込んでいないこと:ブラウザごとの対応状況・対応バージョン、dvh のパフォーマンスコストの実測値。情報源にこれらの数値はなく、今回ブラウザ互換性データの確認も行っていません。また、JavaScript による従来の高さ補正が不要になったかどうかについても、参照した2つの情報源はその比較をしていないため、本記事では判断していません。

単位を選ぶときに立ち返る問い

立ち返りたいのは、「この単位は何を約束し、何を約束していないのか」という問いです。vh は「UIが引っ込んだ状態の高さ」を約束しており、「見えている高さ」を約束していません。はみ出しは、約束されていないものを期待したときに現れます。

もう1つは、「症状が消えたとき、代償はどこへ移ったのか」という問いです。単位の入れ替えで消える症状の裏側には、別の時間軸に現れる挙動があります。最初の描画で確認を終えると、移った先が見えません。

CSS の単位は、計測方法の選択肢ではなく前提の宣言です。どの前提を選んだかは、100vh という記述そのものには残りません。残すとすれば、それは問い1と問い2に自分で答えた記録のほうです。

参考資料・引用元

よくある質問(FAQ)