知っておくと便利なCSSの単位: ビューポートに基づく相対単位(vw, vh, lvh, svh, dvh, vb, viなど)の便利な使い方を解説
Post on:2024年3月28日
CSSでよく使用する単位といえば、px
, r(em)
あたりでしょうか。CSSはそれら以外にもたくさんの単位が使用できます。
前回はフォントに基づく相対単位でしたが、今回はビューポートに基づく相対単位(vw
, vh
, lvh
, svh
, dvh
, vb
, vi
など)の便利な使い方を紹介します。
Relative length units based on the viewport -Going beyond pixels and (r)ems in CSS
by Brecht De Ruyte
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
はじめに
シリーズの第2弾となるこの記事では、ビューポート単位と呼ばれるビューポートに基づく単位について解説します。多くのデベロッパーは知っていることですが、特にスクロールバーやスマホで使用すると予期せぬ動作を引き起こすことがあります。ビューポート単位のこれらの落とし穴についても解説します。
前回と同様に、MDNで簡単に見つけられるCSSで利用可能な単位をリスト化するのではなく、それらの単位がどのようなシーンで役立つのか、例を挙げて解説しようと思います。そして、一般的に起こりうる落とし穴にもフォーカスを当てたいため、特定の単位についてはすこし異なるアプローチをとります。
前回の第1弾はこちら、フォントに基づく相対単位です。
では、第2弾となるビューポートに基づく単位を解説します。
CSSの単位: vwとは
CSSのvw
単位とは、ビューポートサイズの幅を表す相対単位です。1vw
はビューポートサイズの幅の1%に相当します。
ビューポートについて解説するには複数のポイントがあります。
レイアウトビューポート(layout viewport)とは、デスクトップのブラウザウインドウまたはデバイスのスクリーンサイズに影響を受けるWebページの表示領域のことです。ただし、デバイスの場合はユーザーのズームやキーボードの開き方などに依存します。これらによりHTMLコンテンツをレンダリングするための利用可能なスペースが決定されます。
1 |
<div class="viewport"></div> |
1 2 3 4 5 6 |
.viewport { position: fixed; inset: 0; outline: 10px dashed green; outline-offset: -10px; } |
初期包含ブロック(initial containing block、以下ICB)は、ルート(<html>
)要素が存在する矩形です。その寸法はレイアウトビューポートに関連付けられており、左上隅は(0,0)です。ICBはルート要素であるため、次のように表示できます。
1 2 3 4 5 6 |
html { width: 100%; height: 100%; outline: 10px solid hotpink; outline-offset: -10px; } |
これを視覚化するには、レイアウトビューポートをグリーンに、ICBをピンクにした下記のデモページをご覧ください。ページをスクロールすると、これらのビューポートがお互いにどのような相関関係にあるかが分かります。
See the Pen
Layout viewport vs initial containing block by coliss (@coliss)
on CodePen.
ICBをheight: 100%;
にすることで、ICB(ピンク)はスクロールに追従して消えてしまいますが、ビューポート(グリーン)はスクロールしてもその場に留まることが分かります。
なぜこれがvw単位に関係するのですか?
仕様によると、viewport-percentageの長さは初期包含ブロック(ICB)のサイズに対する相対的なものです。ICBの高さや幅が変更されると、それに応じて拡大縮小されます。
つまり、一般的にはこの相対的な長さの単位は、ICBがビューポート上で最初に拡大縮小されるときのビューポート幅のパーセントを表します。たとえば、ビューポートの幅が1000px
の場合、1vw
は10px
(1000pxの1%)に相当します。この仕組みを理解しておくことは、よいことです。
100vwとデベロッパーのフラストレーション
これは、CSSを書いている人たちにとってよくあるフラストレーションです。絶対配置で幅を100vw
に設定すると、横スクロールが表示されることがであります。この動作は、スクロールバーが固定されている場合にのみ表示され、オーバーレイタイプのスクロールバーでは表示されません。macOSでこれを有効にするには、「システム設定 > 外観 > スクロールバーを表示」で、「常に表示」をチェックします。デフォルトでは「マウスまたはトラックパッドに基づいて自動的に表示」にチェックされています。
Tips: 私は「常に表示」をチェックするようにしています。これは制作中にこういった問題を発見できるようにするためです。
これはよくあるフラストレーションで、簡単な解決方法は幅を100%
にすることです。それができない場合は、スクロールバーの幅を計算する方法があります。100vw
を計算し、そこからスクロールバーの幅分を減算します。
この現象が起きる原因は、仕様にあります。どのような場合でもスクロールバーは存在しないと仮定されています。ただし、ICBのサイズは、ビューポート上のスクロールバーの存在に影響されることに注意してください。
横スクロールバーが表示されてしまうデモページを作成しました。macOSの場合は前述の「常に表示」をチェックしてからご覧ください。
See the Pen
100vw horizontal scroll example by coliss (@coliss)
on CodePen.
では、vw
単位はどこに使用するといいのでしょうか?
通常はアイテムのサイズ調整ですが、フォントサイズとしても使用できます。ただし、アクセシビリティの問題が発生する可能性があることに注意してください。フォントサイズにvw
単位を使用する興味深い例は、CSSのclamp()
関数を使用した流動的なタイポグラフィです。
1 2 3 4 5 |
body { font-family: sans-serif; font-size: clamp(1rem, 0.8696rem + 0.6522vw, 1.375rem); line-height: 1.5; } |
See the Pen
Simple clamp() example by coliss (@coliss)
on CodePen.
このデモページでは、サイズ変化の勾配を作成するためにvw
を使用していますが、ほかの単位も使用できます。この流動的タイポグラフィの素晴らしい出発点はutopia.fyi(vi
単位を使用しています)です。場合によっては、vh
単位を使用した流動的タイポグラフィにも大いに意味があります。では、vh
単位についてみてましょう。
clamp()
関数を使用したフォントサイズの設定について詳しくは、下記をご覧ください。
最近よく使用されているCSSの実装テクニック! レスポンシブ対応のフォントサイズをclamp()で超簡単に定義できるツール
CSSの単位: vhとは
CSSのvh
単位とは、vw
がビューポートの幅を表すのと同様に、ビューポートサイズの高さを表す相対単位です。1vh
はビューポートサイズの高さの1%に相当します。たとえば、要素の高さを50vh
に設定すると、ビューポートの高さの半分になります。これは前述したように、実際にはICB(初期包含ブロック)の高さであり、デフォルトではビューポートのサイズです。
このビヘイビアはデスクトップで見ている分にはまったく問題がないのですが、スマホでこのvh
単位を使用すると「何かが起こります」、そしてあまり好ましくありません。
たとえば、スマホでページロード後にスクロールすると、アドレスバーなどの一部のアイテムが移動したり崩れたりするといった特殊なビヘイビアが発生することがよくあります。
実際に試してみましょう。まずは、HTML。
1 2 3 |
<div class="vh"> <h2>This item is at the bottom of 100vh</h2> </div> |
そして、CSSは下記のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.vh { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; width: 100%; height: 100vh; background: skyblue; } body { margin: 0; } |
これにより、下記のビヘイビアが発生します
デスクトップ(Safari for macOS)での表示
スマホ(Safari for iOS)での表示
スマホでは、ページロード後にテキストが下部に表示されていないことに注目してください。これはアドレスバーが折り畳まれた場合、100vh
はビューポートの高さの100%
になるからです。つまり、場合によっては期待する表示にならない可能性があるということです。
幸いなことに、この問題には解決策(この記事の下の方)がありますが、まずは他の単位にすることを検討してみてください。
CSSの単位: vbとviとは、ブロックとインライン
CSSのvb
単位とは、多言語レイアウトを完璧に処理するためのvw
およびvh
の論理的な対応物です。vb
単位はルート要素のブロック方向に最初に含まれるブロックの大きさの1%、vi
単位はルート要素のインライン方向に最初に含まれるブロックの大きさの1%です。
この2つの単位は欧米では、インライン軸は横書き、ブロック軸は縦書きになります。前述したように、utopia.fyiはこれらの論理変数を使用しています。
CSSの単位: vmin, vmaxとは
CSSのvmin
単位とはビューポートの幅と高さの小さい方に等しく、vmax
単位とは大きい方に等しい値になります。これは縦向きと横向きの状況を切り替えるときになどに便利です。
vmin
とvmax
の違いを簡単に視覚化したデモページをご覧ください。ウインドウの表示サイズを変えるとボックスのサイズも変化します。
See the Pen
vmin and vmax playground by coliss (@coliss)
on CodePen.
この単位を使用した便利な例として、グリッド内のギャップがあります。gap
プロパティの値にvmax
を使用しており、ウインドウの表示サイズを変更して試してみてください。
See the Pen
Using ch unit for responsive behaviour by coliss (@coliss)
on CodePen.
CSSの単位: lvh, svh, dvhとは
前述したスマホのフラストレーションを解決してくれる単位について解説します。これらの単位にはl
, s
, d
の接頭辞がついた形式で上記のすべての単位に追加することができます。
l
(large): 大きいビューポートのことで、アドレスバーやツールバーなどの非表示または拡張される可能性のある動的要素を含まないビューポートサイズを表します。s
(small): 小さいビューポートのことで、動的要素を考慮したビューポートサイズを表します。d
(dynamic): 大きいビューポートと小さいビューポートの差を計算し、要素の表示または非表示に合わせて調整する動的単位を提供します。
下記はスマホのSafariで表示した場合のheight: 100svh;
とheight: 100lvh;
の例です。下にスクロールすると、lvh
はウインドウの高さにアドレスバーの高さを加えたサイズであることが分かります。グリーンのアウトラインは視覚的に分かりやすくするために2つの要素を囲んでいます。
ピンク: height: 100svh;
、パープル: height: 100lvh;
デモページ(スマホでご覧ください)
スマホで100vh
が高さいっぱいにならない問題について詳しくは、下記をご覧ください。
CSSの100vhがスマホで高さいっぱいにならない問題はこれで解決! CSSの新しいビューポート単位の使い方と注意事項
ビューポート単位の解説動画
ビューポート単位についてもう少し詳しく知りたい場合は、BramusとJakeによる解説動画がお勧めです。私が最初に「ICB」という用語について知ったのはこの動画でした。
Level 4で定義されている単位の仕様は、CSS Values and Units Module Level 4をご覧ください。
sponsors