vh, dvh, svh, lvh の違いと使い分け
はじめに
vh
、dvh
、svh
、lvh
はすべてビューポートの高さ(viewport height)を基準にしたCSS単位ですが、それぞれ異なる条件で高さを計算します。今まで何となく使っていたので自己学習の為、本記事ではそれぞれの違いを整理し、適切な使い分けについて説明します。
ViewPort単位の進化
vh
単位は2012年に CSS Values and Units Module Level 3 で導入されました。当時はデスクトップブラウザが主流であり、モバイルでの閲覧はそれほど一般的ではありませんでした。しかし、モバイルデバイスの普及に伴い、以下のような問題が発生しました。
- モバイルブラウザのUIバー(アドレスバーなど)の表示・非表示による実際の表示領域の変化
- iOS Safariでのスクロール時のちらつき
- 実際の表示可能領域と計算値の不一致
これらの課題を解決するため、2022年に CSS Values and Units Module Level 4 で新しい単位(dvh
、svh
、lvh
)が導入されました。
各単位の違い
vh
(Viewport Height)
- 基本的なビューポートの高さの単位
- ブラウザのUIを含まない表示領域の高さを基準
- スクロール時のUIの変化を考慮しない
dvh
(Dynamic Viewport Height)
- モバイルブラウザのUIの状態に応じて動的に更新
- スクロール時のUIの表示・非表示を反映
- 実際の表示領域をより正確に取得可能
svh
(Small Viewport Height)
- ブラウザUIが完全に表示された状態での最小ビューポート高さ
- モバイルでのアドレスバーが表示された状態を基準
- 安定した最小サイズの保証に有効
lvh
(Large Viewport Height)
- ブラウザUIが非表示の状態での最大ビューポート高さ
- スクロール時にアドレスバーが隠れた状態を基準
- 固定要素の配置に適している
実装例と検証
const Unit = ['vh', 'dvh', 'svh', 'lvh'] as const;
export function Sample() {
const [unit, setUnit] = useState<(typeof Unit)[number]>('vh');
return (
<div className="w-full">
{/* ヒーローセクション */}
<div
className={`flex flex-col items-center justify-center text-white text-3xl font-bold transition-all
${unit === 'vh' ? 'h-[100vh]' : unit === 'dvh' ? 'h-[100dvh]' : unit === 'svh' ? 'h-[100svh]' : 'h-[100lvh]'}
relative`}
>
{/* 画像 */}
<div className="absolute top-0 left-0 w-full h-full overflow-hidden">
<Image src="https://picsum.photos/800" alt="Hero Image" layout="fill" objectFit="cover" priority />
</div>
<h1 className="relative z-10">Hero Section ({unit})</h1>
</div>
{/* ボタンで単位を切り替え */}
<div className="mt-4 flex justify-center gap-2">
{Unit.map((u) => (
<button
key={u}
onClick={() => setUnit(u)}
className={`px-4 py-2 rounded-lg border ${unit === u ? 'bg-gray-800 text-white' : 'bg-gray-200'}`}
>
{u}
</button>
))}
</div>
</div>
);
}
このコードを React × TypeScript × Tailwind CSS
環境で実行することで、各単位の違いを視覚的に確認できます。(Next.jsのImage
コンポーネントを使用しているため、React単体では修正が必要です。)
検証内容
vh / dvh / svh / lvh
を切り替えたときの表示領域の変化vh
では不要なスクロールが発生するか?
期待する動作
単位動作概要vh100vh
を適用。モバイルでアドレスバーが表示されている場合、余白やスクロールが発生する可能性あり。dvh100dvh
を適用。アドレスバーの表示/非表示に応じて高さが変化。svh100svh
を適用。アドレスバーが表示されているときの高さを基準にする。lvh100lvh
を適用。アドレスバーが非表示のときの最大高さを基準にする。
どのように使い分けるべきか?
以下、最新のブラウザ環境を前提とした使い分けの指針です。
dvh
を使うべき場合
- モバイルファーストのデザイン
- フルスクリーンのヒーローセクション
- モーダルやオーバーレイ
svh
を使うべき場合
- モバイルでの重要なコンテンツ表示
- UIバーを考慮した確実な表示が必要な場合
lvh
を使うべき場合
- 固定ヘッダーやフッター
- スクロールに関係なく一定のサイズを保ちたい要素
vh
を使うべき場合
- 基本的には
dvh
を使用すればよいが、パフォーマンス面で影響がある場合にはvh
も検討 - アニメーションを多用する場合、
vh
の方が安定する可能性あり(要検証)
まとめ
2025年1月時点で、新しいビューポート単位(dvh
、svh
、lvh
)は主要なブラウザ(Chrome、Safari、Firefox、Edge)でサポートされており、積極的に使用することが推奨されています。それぞれの特性を理解し、適切に使い分けることで、より良いUI/UXを実現できそうです。