CSSのサイズ指定時の単位について、というか%について詳しく述べる

CSSのサイズ指定時の単位について、というか%について詳しく述べる

モチベーション

今、仕事でフロントエンドエンジニアをやっているけど、案外CSSが苦手な人が多い。CSSスタイルシート言語なので他のプログラミング言語パラダイムがまるで当てはまらないことが大きな理由の一つなのかなと思う。
特に細かい仕様を頭に入れていないと何やねんみたいな挙動することも多い。
僕はCSSの肝は以下の3点だと考えていて、それさえマスターすればあとはプロパティを死ぬ程覚えるだけでなんとでもなるんでは?と思っている。

  • ボックスモデル
  • レイヤー
  • 単位

というわけでこの内の単位について、忘れないうちに記述しておこうと思う。

単位の種類

単位の種類は他にもあるだろうけど、主に以下の種類がある

  • px
  • em
  • rem
  • vw
  • vh
  • vmin
  • vmax
  • %

それ以外にも様々あるので、上記以外のものも含むざっくりとした説明は以下の記事を見たらわかる。
[CSS]長さの単位

というか、ちゃんと調べたいのであれば仕様書はこれです。
Distance Units: the type

そういうわけで具体的に見ていく

px

言わずもがな。
pxはwebの世界では絶対値としての地位を確立していると思う。絶対値といえばこれ。最も多くのシーンで使われているわかりやすい単位。
そういうわけで特に説明はないです🥰

em

pxは絶対値だったけど、これ以降に出てくるものはすべて相対値になる。
相対値とは、何かの値をもとに決まる値のこと。emの場合はそのコンテキストのフォントサイズで値が決まる。

<div class="target"></div>
.target {
  font-size: 20px
  height: 2em; /* 40px */
}

使い所

コンテキストに完全に依存するので割と使いにくいイメージがあるけど、逆にいえばコンテキストに依存させたいようなときはいいかもしれない。
例えば、ブログなんかの文字は1行あたりざっくり40文字未満が読みやすいらしいので、そういうときは

.entry {
  width: 40em;
}

のようにしておくと、勝手に(font-size * 40)pxを計算してくれる。それ以外にも最大で3行だけ表示したいときの高さなんかも以下のような感じで指定できる。

.block {
  overflow: hidden;
  line-height: 1.6;
  height: calc(3em * 1.6) /* line-heightを掛けてやる */
}
  • box-sizing: border-boxの場合でpaddingを上下にとっている場合はそれも勘案してやる必要がある。

rem

root要素のfont-sizeを基準にした相対値。
root要素ってのはなにかというとhtmlのことです。

html {
  font-size: 62.5%; /* フォントサイズはデフォで16pxなので16 *  0.625 = 10px */
}

body {
  font-size: 1.4rem; /* 14px */
}

こんな感じでメインで使う場合はhtmlにfont-size: 62.5%;として、10px基準で扱い、pxの代わりに扱うことが多い。

pxかremか

戦争がおきそうなテーマでもあるし、僕も一度この件でtwitterで争いがおきたことがあったけど、やっぱ用途としてはpxの代わりという感じ。 ブラウザの機能ではフォントのサイズを変更することができる。これを変更した時にブラウザはhtml要素のfont-sizeを変更するので、remで指定しておくことでその変更を各要素のサイズに反映することが可能になる(そのために上記の指定ではhtmlのfont-sizeは%指定がされている)。
または今後2000pxを大きく超える閲覧環境が出てくるような場合、そのサイズに応じてhtmlのfont-sizeを変更してやれば閲覧環境に合わせたサイズで対応することも可能になる。
ただ、フォントサイズの変更も今どき単純にzoom機能を使うのでは?という気もするので、どこまで考えるかは要件次第って気もするけども。

rem を指定する場合の注意点

上記の通り、remだとサイズが可変することがある。
つまりここはpx、ここはremみたいに混ざってると上記のようなときに可変する場所としない場所が出てわけわからんことになるので、基本的にはpxかremかは統一したほうがいい。(多くの場合emと%はどれとも共存できるとおもう)。
逆にpxだとサイズが可変しないことから、remを使用している場合でも可変してほしくないborder-widthなんかはpxを指定するとかはありだと思う。

vw, vh

viewportのwidth、またはheightをもとにした相対値。
100vw = 画面の横幅という感じ。1vwなら画面の横幅の1%の値。

使いどころ

これはちょいちょい使う。例えばファーストビューにheight: 80vhみたいにしたりとか。
あとはスマホ時にすべてのサイズをvwで指定することによって、デバイスのサイズが変わっても全く同じ見た目にするみたいなこともできる。代理店案件とかだと、デバイスが変わることによって、文字の入り方が変わるの嫌がられるみたいな闇があるらしく、そういう時に使ってたみたいな地獄みたいな話も聴いたことある。(その場合はぶっちゃけviewportを固定値にしてやれば済む話だけど)

body {
  font-size: 3.73333333vw /* 14 / 375 */
}

iframe内のvw, vhの値

iframe内の要素にvw, vhがあたっている場合は、iframeのサイズが基準になる。
そりゃそうかという感じだけど、stack overflowにそういう質問が上がっていた。

CSS vh units inside an iframe

vhの問題点

vhは便利だけど、スマホのブラウザだとアドレスバーの表示非表示によって若干意図しない挙動になる場合がある。
The trick to viewport units on mobile
そういう場合はおとなしくJavaScriptに頼るのが話が早そう。

vmin, vmax

viewportの短い方、長い方の値を基準にした値ということらしい。
全然使ったことない。

%

%は非常にややこしい。というかこの記事は基本的に%を説明したくて書いているので、ようやく本番がきたなと言う気持ち。 それではやっていくぞ💪

そもそも%ってなんやねん

今までのものは明確な基準値のある相対値だった。それはたとえば、font-sizeだったりview-portだったり、ある値をもとに数値が決定されていたわけだけど、この%というものに関しては付与するプロパティや状態によって基準値が変わる。
整理した感じ4種類あるので順にみていく。

1. コンテキストの相対値としての%

font-sizeのパターン。当たるはずの値を基準に割合を決定する。

body {
  font-size: 20px;
}

h1 {
  font-size: 200%; /* 40px */
}

逆にfont-size以外思いつかん。

2. 親の表示領域の相対値パターン

  • width
  • height
    • margin
    • padding

width、heightに関してはそうかなという感じがすると思う。親の表示領域の中での割合が値になる。
ただ、marginとpaddingに関しては親の表示領域の中でもwidthのサイズを基準とした値になる。

.parent {
  width: 600px;
}

.child {
  margin-top: 100%; /* 600px */
}

この特性をうまく使うと画像の縦横比を固定することができる。

.fixRatio {
  display: block;
  width: 100%;
}

.fixRatio::before {
  content: '';
  display: block;
  padding-top: 75%; /* 横幅に対して75%の高さ */
}

どっちかにbackground-imageなんかを指定したり、.fixRatioposition: relativeして中でimg要素をposition: absoluteしたりして使う。
結構よく使うイディオム。

親レイヤーを基準にするパターン

position: absoluteのときの%のパターン。
このときは祖先で1番近縁のposition: relativeを持つ要素のボックスを基準に相対値が決まる。 (position: fixedのときはhtml?なのかな?)

  • top, left, right, bottom
  • width
  • height

自分のサイズに依存するパターン

transfromプロパティのtranslateがこれ。
自分のサイズに依存する。

.block {
  width: 600px;
  height: 200px;
  transform: translate(50%, 50%); /* x方向に300px, y方向に100px移動する */
}

この特徴を活かして、中央表示なんかによく使われる。
全然どうでもいいことだけど、例えば以下のようにしてblock要素を左右中央表示することも出来る。

.block {
  margin-left: 50%;
  transform: translate(50%, 50%);
}

まぁ、margin: autoでいいんだけど。

%が使えないプロパティ

以下のやつは使うことができない。

  • border-width
  • box-shadowのカラー以外の値
  • opacity

多分他にもありそう。

そういうわけで

単位と仲良くなりましたか?
これでCSSと戦っていきまっしょう💪