Vinyl

vh, dvh, svh, lvh の違いと使い分け

はじめに

vhdvhsvhlvh はすべてビューポートの高さ(viewport height)を基準にしたCSS単位ですが、それぞれ異なる条件で高さを計算します。今まで何となく使っていたので自己学習の為、本記事ではそれぞれの違いを整理し、適切な使い分けについて説明します。

ViewPort単位の進化

vh 単位は2012年に CSS Values and Units Module Level 3 で導入されました。当時はデスクトップブラウザが主流であり、モバイルでの閲覧はそれほど一般的ではありませんでした。しかし、モバイルデバイスの普及に伴い、以下のような問題が発生しました。

  • モバイルブラウザのUIバー(アドレスバーなど)の表示・非表示による実際の表示領域の変化
  • iOS Safariでのスクロール時のちらつき
  • 実際の表示可能領域と計算値の不一致

これらの課題を解決するため、2022年に CSS Values and Units Module Level 4 で新しい単位(dvhsvhlvh)が導入されました。

各単位の違い

  1. vh (Viewport Height)
  • 基本的なビューポートの高さの単位
  • ブラウザのUIを含まない表示領域の高さを基準
  • スクロール時のUIの変化を考慮しない
  1. dvh (Dynamic Viewport Height)
  • モバイルブラウザのUIの状態に応じて動的に更新
  • スクロール時のUIの表示・非表示を反映
  • 実際の表示領域をより正確に取得可能
  1. svh (Small Viewport Height)
  • ブラウザUIが完全に表示された状態での最小ビューポート高さ
  • モバイルでのアドレスバーが表示された状態を基準
  • 安定した最小サイズの保証に有効
  1. 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 を適用。アドレスバーが非表示のときの最大高さを基準にする。

どのように使い分けるべきか?

以下、最新のブラウザ環境を前提とした使い分けの指針です。

  1. dvh を使うべき場合
  • モバイルファーストのデザイン
  • フルスクリーンのヒーローセクション
  • モーダルやオーバーレイ
  1. svh を使うべき場合
  • モバイルでの重要なコンテンツ表示
  • UIバーを考慮した確実な表示が必要な場合
  1. lvh を使うべき場合
  • 固定ヘッダーやフッター
  • スクロールに関係なく一定のサイズを保ちたい要素
  1. vh を使うべき場合
  • 基本的には dvh を使用すればよいが、パフォーマンス面で影響がある場合には vh も検討
  • アニメーションを多用する場合、vh の方が安定する可能性あり(要検証)

まとめ

2025年1月時点で、新しいビューポート単位(dvhsvhlvh)は主要なブラウザ(Chrome、Safari、Firefox、Edge)でサポートされており、積極的に使用することが推奨されています。それぞれの特性を理解し、適切に使い分けることで、より良いUI/UXを実現できそうです。