うぇぶわんこ
height: 100vhなのに、スマホだけ下が切れるワン……。100dvhに変えたら全部解決?
さとけん
それで直る場合もあります。でも、全部をdvhにするとHeroまでスクロール中に高さが変わることがあります。まず「その要素の高さも動いてほしいか」を考えましょう。
height: 100vh;でファーストビューを作った。PCでは問題ないのに、iPhoneなどのスマートフォンで見ると下のボタンが少し見切れたり、余計な縦スクロールが出たりする。そんなときに使えるのがsvh・lvh・dvhです。ただし、100vhで問題が起きたからといって、すべて100dvhへ置き換えればいいわけではありません。この記事では、スマホで100vhがずれる理由から、用途ごとのviewport単位の選び方、heightとmin-heightの判断まで実例と一緒に整理します。
この記事でわかること
height: 100vhなのにスマホでは画面内に収まらない
例えば、画面いっぱいのファーストビューを作るために、次のようなCSSを書くことがあります。
.hero {
height: 100vh;
display: grid;
place-items: center;
}
HTMLも特別なものではありません。
<section class="hero">
<div class="hero__inner">
<h1>WEB DESIGN & DEVELOPMENT</h1>
<p>ホームページ制作をもっと身近に。</p>
<a href="#">お問い合わせ</a>
</div>
</section>
PCブラウザでは画面いっぱいに見えていても、スマートフォンではCTAが少し見切れたり、Heroをすべて見るために少しスクロールが必要になったりすることがあります。
まず外したい思い込み
100vh = 「今、この瞬間に見えている部分の高さ」とは限りません。
現在のCSS仕様では、デフォルトのvhはlarge viewportを基準にします。スマートフォンでアドレスバーやツールバーが表示されている状態では、実際に見えている領域より100vhの方が大きくなることがあります。
つまり、100vhそのものが壊れているわけではありません。基準にしているviewportと、その瞬間に見えている領域が違うことが、見切れにつながります。
なぜこんなことが起こる?スマホには「高さ」が複数ある
PCブラウザでは、ウィンドウサイズを変更しない限り表示領域の高さは大きく変わりません。一方、スマートフォンにはアドレスバー・ツールバー・タブバーなどのブラウザUIがあり、スクロールによって縮小したり隠れたり、再び表示されたりします。
そのためCSSでは、ブラウザUIの状態を考慮した3種類のviewportを扱えます。
名前だけを見ると少し難しく感じますが、この記事では「小さい高さ・大きい高さ・現在の高さ」と考えます。
svh・lvh・dvhは何が違う?
svh|ブラウザUIが表示された「小さい高さ」
svhはSmall Viewport Heightの略です。ブラウザのアドレスバーなどが表示され、Webページが使える領域が小さくなっている状態を基準にします。
.hero {
min-height: 100svh;
}
ページを開いた直後のファーストビューに、見出し・説明文・CTAまでなるべく収めたい場合に使いやすい単位です。また、ブラウザUIが隠れたときに、その変化へ追従してHero自体が伸び続ける単位ではありません。
そのため、「最初の表示領域へ収めたい。でもスクロール中にHeroの高さまでは変わってほしくない」というケースと相性がよいことがあります。
lvh|ブラウザUIが隠れた「大きい高さ」
lvhはLarge Viewport Heightの略です。ブラウザUIが引っ込んだ状態の大きいviewportを基準にします。
.visual {
min-height: 100lvh;
}
背景画像やビジュアルを最大領域まで大きく見せたい場合には使いやすい一方、ブラウザUIが表示されている状態では一部が現在見えている領域から外れる可能性があります。
dvh|「現在の高さ」に合わせて変化する
dvhはDynamic Viewport Heightの略です。svhやlvhとの大きな違いは、ブラウザUIの状態に合わせて値が変化することです。
.modal {
height: 100dvh;
}
アドレスバーなどが表示されているときはsmall viewportに近づき、ブラウザUIが隠れるとlarge viewportに近づきます。その時点で利用できる表示領域へ追従したいUIでは便利ですが、高さが動くこと自体がメリットにもデメリットにもなります。
100vhと100lvhは何が違う?
ここはsvh・lvh・dvhを理解するときに押さえておきたいポイントです。
現在のCSS仕様では
vhはlvhと同じlarge viewportを基準にします。
つまり次の2つは、基本的に同じ基準の高さです。
.element-a {
height: 100vh;
}
.element-b {
height: 100lvh;
}
4種類がそれぞれ別々の高さだと考えるより、svh ←→ dvh ←→ lvh = vhと整理すると分かりやすくなります。
100vhでスマートフォンのファーストビューが見切れることがあるのも、ブラウザUIが表示されている小さい状態ではなく、large viewportを基準にしているためです。
結局どれを使えばいい?単位より「どう動いてほしいか」で考える
単位の意味を覚えても、実際の制作では「結局どれを使えばいいの?」となります。私は、単位の新旧からではなく、ブラウザUIが変わったときに、その要素の高さも変わってほしいかから考えるようにしています。
ファーストビューなら100svhを候補にする
.hero {
min-height: 100svh;
}
最初の画面内へメインコピーやCTAを収めたいHeroで、スクロール後にHeroの高さまで伸縮させる必要がないなら、svhが候補になります。
モーダルやドロワーなら100dvh
.modal {
height: 100dvh;
}
「今、実際に使える表示領域へ合わせたい」UIなら、dvhの動く性質がメリットになります。画面いっぱいのドロワーメニューも同じ考え方です。
Webアプリ風のレイアウトでも100dvhが候補
.app {
height: 100dvh;
overflow: auto;
}
チャット画面や管理画面など、ブラウザの表示領域全体をひとつのアプリUIとして扱う場合にも候補になります。
最大領域までビジュアルを広げたいならlvh
.visual {
min-height: 100lvh;
}
重要な情報を必ず画面内へ収めることより、背景画像やビジュアルを最大領域まで見せたい場合にはlvhが候補になります。既存コードの100vhもlarge viewport基準で問題がなければ、無理に変更する必要はありません。
何でも100dvhに置き換えればいいわけではない
dvhを知ると、height: 100vh;を見つけるたびに100dvhへ変更したくなるかもしれません。ですが、それではdvhの特徴を生かしているというより、単に新しい単位へ置き換えているだけです。
.hero {
min-height: 100dvh;
}
スマートフォンで下へスクロールし、アドレスバーなどが縮小・非表示になって利用できるviewportが大きくなると、dvhもその変化へ追従します。そのため用途やブラウザの挙動によっては、スクロール中にHeroが伸縮して見えることがあります。
CSS仕様でもdynamic viewportのサイズは安定せず、使用方法によってはスクロール中のサイズ変更がユーザーにとって気になる動きやパフォーマンス上のコストにつながる可能性があるとされています。ただし、「dvhを使うと必ずガクつく」という意味ではありません。
/* 最初の画面内へ収めつつ、高さを安定させたい */
.hero {
min-height: 100svh;
}
/* 現在見えている高さへ追従させたい */
.modal {
height: 100dvh;
}
この記事で一番伝えたいこと
新しい単位だからdvhではなく、動いてほしいからdvh。
viewport単位だけでなく、heightかmin-heightかも確認する
100vh問題を修正するとき、単位だけでなくheightとmin-heightも見ておきたいところです。
.hero {
height: 100svh;
}
これではHeroの高さが100svhに固定されます。制作後に説明文・CTA・お知らせ・キャンペーンなどが増えた場合、内容が窮屈になったり、はみ出したりする原因になります。
.hero {
min-height: 100svh;
}
min-heightなら最低でもviewportの高さを確保しつつ、コンテンツが増えればHero自体も伸びられます。
単位だけを入れ替えない
「vhをsvhかdvhにする?」だけでなく、そもそもheightで固定する必要があるのかまで確認します。
dvhでもソフトウェアキーボード問題まで解決するわけではない
dvhを「スマートフォンで今見えている高さを全部自動で解決する単位」と考えると、フォーム入力時に想定と違う動作になることがあります。
スマートフォンでinputやtextareaへフォーカスすると、画面下からソフトウェアキーボードが表示されます。ただし、オンスクリーンキーボードは通常、large / small / dynamic viewportを分けるブラウザUIとは別に扱われます。
.form-modal {
height: 100dvh;
}
これだけで、キーボード表示時の入力欄や送信ボタンまで必ず見切れなくなるわけではありません。フォームを含むモーダルやチャットUIでは、実際にフォーカスしてキーボードを表示した状態でも操作できるかを実機で確認します。
対応ブラウザは?今でもフォールバックは必要?
svh・lvh・dvhはChrome 108、Firefox 101、Safari 15.4以降で主要ブラウザエンジンに対応し、2025年6月にはBaseline Widely Availableになりました。2026年時点では「新しいから避ける」より、案件の対応ブラウザに合わせて使う単位と考えやすくなっています。
古い環境も考慮する案件なら、従来のvhを先に書く方法があります。
.hero {
min-height: 100vh;
min-height: 100svh;
}
svh非対応環境では最初の100vhが残り、対応環境では後に書いた100svhが適用されます。何となく古いブラウザ対策を増やすのではなく、案件で必要なサポート範囲を確認して判断します。
スマホの100vh問題はDevToolsだけで終わらせない
viewportまわりを修正したら、最後はスマートフォン実機でも確認します。特に次の一連の動きを見ます。
- ページを開いた直後を見る
アドレスバーが表示された状態でCTAやHero下端が見切れていないか確認します。 - 下へスクロールする
ブラウザUIが縮小・非表示になったときにHeroが不自然に伸縮しないか確認します。 - 再び上へ戻る
ブラウザUIが戻ったときに不要なスクロールやレイアウト変化がないか確認します。 - モーダルやドロワーを開く
上下が見切れず、内部スクロールも意図どおりか確認します。 - フォームがあればキーボードも表示する
入力欄や送信ボタンが操作できるか確認します。
DevToolsのデバイス表示は便利ですが、スマートフォンブラウザ特有のUI変化まで含めると、実機で気づきやすい問題もあります。
また、viewport単位の問題だと思っていたけれど、実際には別のCSSでheightが上書きされていた、ということもあります。
CSSそのものが意図どおり適用されているか確認したい場合は、「CSSが効かないとき、私はまずここを見る。DevToolsで確認する7つの場所」でも確認方法をまとめています。
参考情報
今回の記事では、large / small / dynamic viewportの定義、vhとlvhの関係、dynamic viewportの注意点、ブラウザ対応についてW3C、MDN Web Docs、web.devの情報を確認しています。
まとめ|100vhを見つけても、とりあえず100dvhにはしない
height: 100vh;でスマートフォンだけ表示が合わない場合、原因のひとつはブラウザUIとviewportの高さの違いです。
svh・lvh・dvhの使い分け
svh・lvh・dvhは、100vhを新しい書き方へ置き換えるためだけの単位ではありません。どの状態のviewportを基準にするかを、制作者側で選ぶための単位です。
95vhや90vhへ数字を微調整する前に、一度こう考えてみてください。
この記事の結論
この要素は、ブラウザUIが動いたとき、高さも一緒に動いてほしいでしょうか?
Contact
スマホ実機まで確認しながら、
あとで困りにくいWebサイトを考えます
KU NEL WEBLEでは、PCの見た目だけでなく、スマートフォン実機での表示やブラウザUIの変化、コンテンツ追加後の保守まで考えながらHTML・CSSを実装しています。
こんなご相談も大丈夫です
「原因は分からないけれど、この画面だけ少しおかしい」という段階でも大丈夫です。見た目だけを合わせるのではなく、何が起きているのかを整理しながら対応します。