CSSにおける動的ビューポート単位の理解
モバイルでフルスクリーンレイアウトを構築した際に、ブラウザのアドレスバーの背後でコンテンツが切り取られる現象に遭遇したことがあるなら、それは典型的な100vh問題です。以前はJavaScriptのハックで対処していましたが、現在はCSSがsvh、lvh、dvhといったモダンなビューポート単位でネイティブに対応しています。
重要なポイント
- 従来の
vh単位はモバイルでは大きいビューポートにマッピングされるため、ブラウザのアドレスバーが表示されているときにコンテンツがオーバーフローします。 - CSSでは現在、大(large)
lvh、小(small)svh、動的(dynamic)dvhの3つのビューポート状態が定義されており、それぞれに対応する単位があります。 - ファーストビューのコンテンツには
svh、没入型のフルスクリーンレイアウトにはlvh、ブラウザUIの変化に対応する適応型デザインにはdvhを使用します。 - 動的ビューポート単位は仮想キーボードには対応していません。入力が多いレイアウトは実機でテストしてください。
- 新しい単位をサポートしていない古いブラウザのために、常に
vhのフォールバックを含めてください。
モバイルでvhが失敗する理由
デスクトップではvhは確実に機能します。モバイルでは、ブラウザのUI(アドレスバー、タブストリップ、ナビゲーションコントロール)がユーザーのスクロールに応じて展開・収縮します。ビューポートの高さは変化しますが、vhはそれに追従しません。
vhは大きいビューポート(large viewport、lvhと同様)を反映し、ブラウザのクロームが完全に格納されていることを前提としています。そのため、アドレスバーが表示されているときに100vhの要素が表示領域をオーバーフローすることがあります。コンテンツが切り取られ、ボタンが到達不可能になり、レイアウトが崩れます。
3つのビューポート状態:Large、Small、Dynamic
CSS Values and Units仕様では、この問題に対処するために3つの異なるビューポート状態が定義されています:
- Large viewport(大きいビューポート) — すべてのブラウザUIが格納されたときに測定(利用可能な最大高さ)
- Small viewport(小さいビューポート) — すべてのブラウザUIが展開されたときに測定(利用可能な最小高さ)
- Dynamic viewport(動的ビューポート) — 現在アクティブな状態を追跡
各状態には対応するCSS単位があります:
| ビューポート状態 | 高さの単位 | 幅の単位 |
|---|---|---|
| Large | lvh | lvw |
| Small | svh | svw |
| Dynamic | dvh | dvw |
各ファミリーにはmin、max、inline、blockのバリアント(dvi、dvb、svmin、lvmaxなど)も含まれています。
重要: モダンブラウザではvhは事実上lvhにマッピングされます。どちらも大きいビューポートサイズを反映します。
動的UIを持たないデスクトップブラウザでは、3つのビューポートサイズはすべて同一です。
svh、lvh、dvhの使い分け
svhを使用する場合: スクロールする前のページ初期読み込み時にコンテンツが完全に表示される必要がある場合。ブラウザのクロームが完全に表示された最小のビューポート内にレイアウトが収まることを保証します。
/* 読み込み時に常にコンテンツが表示され、スクロール不要 */
.above-the-fold {
min-height: 100svh;
}
lvhを使用する場合: ゲーム、スプラッシュスクリーン、アプリシェルなど、没入型のフルスクリーン体験で、利用可能な最大スペースを埋めたい場合。
/* ブラウザのクロームが非表示のときに画面を埋める */
.fullscreen-app {
height: 100lvh;
}
dvhを使用する場合: ブラウザUIの変化に応じてレイアウトを適応させたい場合。ほとんどのレスポンシブレイアウトにとって最も実用的な選択肢です。
/* モバイルで問題あり */
.hero { height: 100vh; }
/* ブラウザのクローム変化に適応 */
.hero {
height: 100vh; /* 古いブラウザ用のフォールバック */
height: 100dvh; /* モダンブラウザはこちらを使用 */
}
上記のフォールバックパターンが機能するのは、dvhを認識しないブラウザが2番目の宣言を無視して最初の宣言を適用するためです。モダンブラウザは最後の有効な宣言を適用するため、100dvhが有効になります。
Discover how at OpenReplay.com.
仮想キーボードに関する注意点
dvhはアドレスバーのリサイズに対応しますが、仮想キーボードには対応していません。ユーザーが入力フィールドをタップして画面上のキーボードが表示されたとき、ほとんどのブラウザはデフォルトでビジュアルビューポートのみをリサイズするため、ビューポート単位は通常調整されません。
キーボードを考慮したレイアウト動作をオプトインするには、interactive-widgetメタタグを使用できます:
<meta name="viewport" content="width=device-width, initial-scale=1, interactive-widget=resizes-content">
resizes-content値は、仮想キーボードが開いたときにビューポートをリサイズするようブラウザに指示します。他の値にはresizes-visual(デフォルト、ビジュアルビューポートのみをリサイズ)とoverlays-content(キーボードがリサイズなしでオーバーレイ)があります。この動作のサポートは現在、主にChromiumベースのブラウザに存在します。
ChromeはVirtualKeyboard APIによるプログラマティックな制御も提供しています。このAPIのクロスブラウザサポートは依然として限定的なため、レイアウトが入力フィールドの下の可視スペースに依存している場合は、実機で慎重にテストしてください。
ブラウザサポート
svh、lvh、dvhは以下でサポートされています:
- Chrome 108+
- Safari 15.4+
- Firefox 101+
モダンブラウザ全体でカバレッジは堅実です。上記で示したheight: 100vhフォールバックパターンは、古い環境を適切に処理します。現在の互換性はMDNのビューポート単位ドキュメントでも確認できます。
適切な単位の選択
- 初期読み込み時にコンテンツが収まる必要がある? →
svh - 没入型のフルスクリーンレイアウト? →
lvh - ブラウザUIに応答する適応型レイアウト? →
dvh - 古いブラウザをサポートする? → 常に
vhのフォールバックを含める
まとめ
CSSにおけるモバイルビューポート高さの問題は、クリーンでネイティブなソリューションを得ました。ほとんどの場合vhをdvhに置き換え、ファーストビューの可視性が重要な場合はsvhを使用し、フルスクリーン体験にはlvhを予約します。後方互換性のために常に新しい単位とvhフォールバックをペアにし、仮想キーボードには別途対応が必要であることを忘れないでください。JavaScriptは不要です。
よくある質問
はい、ただし注意が必要です。dvhはブラウザUIが展開・収縮するにつれて変化するため、dvhでサイズ設定された固定要素はスクロール中に目に見えてシフトする可能性があります。固定高さの固定ヘッダーやフッターには、静的なピクセルやrem値を使用する方が通常より予測可能です。dvhは小さな固定サイズ要素ではなく、フルハイトコンテナに使用してください。
ほとんどのレイアウトでは、パフォーマンスへの影響は無視できます。ただし、複雑なレイアウト再計算をトリガーする深くネストされた多数の要素にdvhを適用すると、ローエンドデバイスで軽微なジャンクが発生する可能性があります。パフォーマンスが重要な場合は、実際のハードウェアでプロファイリングしてください。
同じロジックに従いますが、ビューポート幅に適用されます。ほとんどのモバイルブラウザでは、アドレスバーが表示・非表示になっても幅は変化しないため、svw、lvw、dvwは通常同じ値を返します。この区別は、サイドパネルや他のUI要素が利用可能な幅に影響を与えるデバイスやブラウザでより重要になります。
ビューポートメタタグにinteractive-widget=resizes-contentを追加してください。これにより、キーボードが開いたときにレイアウトビューポートを縮小するようブラウザに指示され、dvhは縮小されたスペースを反映します。この設定がない場合、キーボードはコンテンツにオーバーレイし、ビューポート単位は変更されません。動作はブラウザやプラットフォームによって異なるため、実機でテストしてください。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.