px・rem・em の違いと使い分け:CSS 単位完全ガイド
説明に入る前に、px・rem・em の結論を先に示す。サイズ指定のほとんどには rem を使う。フォントサイズ、padding、margin、gap、border-radius、ブレークポイント、いずれも読者のブラウザのフォントサイズ設定に合わせて拡大縮小するからだ。決して拡大縮小すべきでない数少ないもの、たとえば 1px のボーダーや正確なシャドウのオフセットには px を使う。要素自身のフォントサイズに合わせて値を伸縮させたい稀な局所ケース、たとえばボタンのテキストに連動する padding には em を使う。
このルールで判断の 90% はカバーできる。問題が潜むのは残りの 10% だ。初めて触れる人を必ず驚かせる em の複合計算、ズーム時にレイアウトを崩すメディアクエリのバグ、そして px のほうがむしろアクセシブルになるケース。この三つを、それぞれ実行可能な CSS とともに見ていく。スタイルを書きながら開いておけるプロパティ別の早見表も後半に置いた。
px・rem・em が実際に意味するもの
三つの単位、三つの異なる基準点。区別はこれがすべてだ。
px は絶対単位である。1 つの px は 1 つの CSS ピクセルであり、周囲に何があろうとそのサイズを保つ。border: 1px solid は 1 ピクセル、それ以上でも以下でもない。落とし穴は、「絶対」がユーザーの設定を無視することも意味する点だ。なぜそれが問題になるかは後述する。
rem はルート要素のフォントサイズを基準にする。ルートは <html> であり、ブラウザはそのフォントサイズをデフォルトで 16px に設定する。したがって標準的な環境では、ページのどこであろうと、入れ子の深さにかかわらず 1rem は 16px に等しい。この一貫性が rem の使いやすさを支えている。基準値が一つだけで、解決結果に意外性がない。
em は現在の要素(font-size 以外のプロパティでは親要素)のフォントサイズを基準にする。基準が要素の入れ子に応じて変わるため、em の値は文脈次第で変動する。同じ 1.5em が、ある場所では 24px、別の場所では 30px に解決され得る。
覚えるべき基準は 16px = 1rem だ。他に何も身につけなくても、これだけは身につけてほしい。特定の値を変換したいときは、px → rem 変換ツールが、選んだ任意のベース値で割り算してくれる。
px・rem・em ひと目で比較
| 単位 | 基準 | ユーザー字号で拡大縮小するか | 典型的な用途 | 入れ子時の挙動 |
|---|---|---|---|---|
px | なし(絶対) | しない | ボーダー、シャドウのオフセット、ヘアライン | 常に同じサイズ |
rem | ルート <html> のフォントサイズ | する | フォントサイズ、間隔、ブレークポイント | 常に同じサイズ |
em | 現在の要素のフォントサイズ | する | コンポーネントに紐づく局所値 | 複合する — ずれ得る |
ほとんどの議論を決めるのは「ユーザー字号で拡大縮小するか」と「入れ子時の挙動」の二列だ。rem はその両方で勝つ。読者の設定を尊重し、かつ予測可能なままでいる。em は前者の利点は共有するが、後者を手放す。
各単位の計算方法
計算自体はただの割り算と掛け算だ。人がつまずくのは、何で割り、何を掛けるかのほうである。
rem はルートのフォントサイズを使う:
rem = px ÷ root-font-size
デフォルトの 16px ルートでは、24px ÷ 16 = 1.5rem。戻すには掛ける:1.5rem × 16 = 24px。ページ上のすべての rem が同じ 16(あるいは設定したルート値)を使う。だからこそ rem は予測可能なのだ。
em は現在の要素自身のフォントサイズを使う:
em = px ÷ current-element-font-size
ある要素の font-size が 20px なら、その要素上で 1em は 20px、0.5em は 10px、1.5em の padding は 30px になる。要素のフォントサイズを変えれば、それに紐づくすべての em 値も連動して変わる。その局所的な結びつきが em の狙いであり、同時にその罠でもある。
em の複合の罠
これは競合がさらりと流す部分だ。フォントサイズに em を使う要素を入れ子にすると、値はツリーを下るにつれて掛け合わされる。各レベルが親の計算済みフォントサイズを継承し、その上に自身の em 係数を適用するからだ。
.menu { font-size: 1.2em; } /* 親が 16px → 19.2px */
.menu .item { font-size: 1.2em; } /* 親が 19.2px → 23.04px */
.menu .item .sub { font-size: 1.2em; } /* 親が 23.04px → 27.648px */
各レベルは「親の 120%」であり、一見無害に聞こえる。だが親がすでに大きくなっているため、三番目のレベルは元の 16px を基準にすると 1.2 × 1.2 × 1.2 = 1.728em、約 27.6px になる。ルールを単独で読んで思い浮かべる 19.2px ではない。リストの中にリスト、さらにコンポーネントを入れ子にすれば、テキストは追跡しづらい形で膨れ上がる。
rem はこれを完全に回避する。1.2rem はドキュメントの最上部にあろうと 12 階層の深さにあろうと 19.2px だ。常に親ではなくルートを基準に測るからである。値が予想外のサイズに解決されたとき、まず問うべきは、それが em(親基準、複合する)か rem(ルート基準、安定)かだ。はぐれた rem をデバッグしていて、そのピクセルサイズを素早く知りたいなら、rem → px 変換ツールが即座に解決してくれる。
rem を使うとき
デフォルトでは rem に手を伸ばす。フォントサイズ、padding、margin、gap、border-radius、メディアクエリのブレークポイントに正しい単位だ。読者がテキストサイズを調整したときに拡大縮小すべきもの、すべてに当てはまる。
最後の一節こそアクセシビリティの論拠であり、それは机上の空論ではない。WebAIM のスクリーンリーダーおよびロービジョン調査は、ブラウザや OS のデフォルトフォントサイズを調整するユーザーが相当な割合に上り、その多くが標準の 16px をはるかに超えて設定していることを一貫して示している。rem でサイズ指定したレイアウトはその変更を尊重する。デフォルトを 20px に上げれば、rem ベースのすべての値が比例して大きくなる。px でサイズ指定したレイアウトはそれを完全に無視する。読者がどれだけ大きくする必要があっても、テキストはハードコードされたサイズに固定されたままだ。
:root {
font-size: 16px; /* 1rem = 16px */
}
h1 { font-size: 2rem; } /* 32px、ユーザー設定に合わせて拡大縮小 */
p { font-size: 1rem; } /* 16px */
.card { padding: 1.5rem; } /* 24px */
.card { border-radius: 0.5rem; } /* 8px */
ここではすべての値が同じルートに固定されているため、ルートのフォントサイズを一度変えるだけでインターフェース全体が比例して再スケールされる。それがデザインシステムの一貫性を保つものでもある。間隔とタイポグラフィがばらばらにずれるのではなく、一緒に動くからだ。
62.5% のテクニック
rem の算数を些細なものにする人気のショートカットがある。ルートのフォントサイズを 62.5%、つまり 62.5% × 16px = 10px に設定する:
html {
font-size: 62.5%; /* これで 1rem = 10px */
}
body {
font-size: 1.6rem; /* 読みやすい 16px の本文に戻す */
}
h1 { font-size: 2.4rem; } /* 24px */
p { font-size: 1.6rem; } /* 16px */
10px ルートなら、頭の中の計算は「ピクセル値を 10 で割る」に集約される。24px → 2.4rem、12px → 1.2rem。唯一の手間は body { font-size: 1.6rem } で読みやすい本文サイズに戻すことだ。生の 10px ベースではデフォルトのテキストが小さすぎるからである。10px ではなく 62.5% をパーセンテージとして使えば相対的に保たれるため、ブラウザのデフォルトをスケールする読者も比例した拡大を得られる。このベースを採用するなら、変換ツールのルートフォントサイズを 10 に設定して、スタイルシートと一致させること。
em を使うとき
値をルートではなく要素自身のフォントサイズに合わせて拡大縮小したいときに em を使う。典型例はボタンだ:
.btn {
font-size: 1rem; /* ルートを基準にサイズ指定 */
padding: 0.75em 1.5em; /* padding はボタンのテキストに連動 */
}
.btn--large {
font-size: 1.25rem; /* 一つの変更ですべてをリサイズ */
}
padding が em なので、.btn--large の修飾子は一つの宣言からテキストとその padding を一緒にリサイズする。ボタンはどのサイズでも比例を保つ。同じ理屈は、隣り合うテキストの行に合わせて em でサイズ指定したアイコンや、フォントに連動して大きくすべき letter-spacing にも当てはまる。
実務で効く戦略は、グローバルな骨格には rem、局所的な比率には em だ。フォントサイズを rem で指定してルートとユーザー設定に応えさせ、その要素に連動すべき少数の値を em で指定する。ただし深く入れ子になるものに em を使わないこと。さもないと先ほどの複合の罠が忍び寄ってくる。
px を使うとき
本当に拡大縮小すべきでない値があり、それらには px が正しい。1px のヘアラインのボーダー、正確な box-shadow のオフセット、2px のフォーカスリング。これらはレンダリングの詳細であって、コンテンツではない。ユーザーがテキストを拡大したときに 1.25px へ「拡大縮小」するボーダーは何の得もなく、ぼやけた線としてレンダリングされ得る。px はそれをくっきり保つ。
.divider { border-bottom: 1px solid; } /* 1px のままであるべき */
.card { box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* 固定のオフセット */
.input:focus { outline: 2px solid; } /* くっきりしたフォーカスリング */
意外なニュアンス — px のほうがアクセシブルなとき
ここがほとんどの「常に rem を使え」というアドバイスが飛ばす、直感に反する部分だ。アクセシブルな選択は「どこでも rem」ではない。「拡大縮小すべきものは拡大縮小させ、すべきでないものは固定する」だ。
1px のボーダーは固定の詳細である。ユーザーがテキストを拡大したときに大きくなるよう rem に押し込んでも、読みやすさの助けにはならない。ヘアラインがぼやけるだけだ。これらのプロパティでは、まさに動かないからこそ px のほうがアクセシブルな選択になる。
人が実際に犯す間違いは逆方向だ。フォントサイズやブレークポイントのような、反応すべきものに px を使ってしまう。そこで px がアクセシビリティを損なう。だからルールは単位の話ではなく、プロパティの話なのだ。その値が読者の操作するコンテンツ(拡大縮小させる — rem)か、固定のレンダリング詳細(固定する — px)かを問う。単位は答えから自ずと決まる。
メディアクエリの罠
これは実際のレイアウトを壊すのに、ほとんどのガイドが警告しない。px で書いたメディアクエリのブレークポイントは、思うようにはブラウザのフォントサイズズームに反応しない。
width: 600px でサイドバーが折りたたまれるブレークポイントを思い浮かべてほしい。視覚に困難のあるユーザーが、快適に読むためにブラウザのデフォルトを 24px に設定する。すると今やコンテンツはより多くの横幅を必要とする。テキストが大きくなったぶん、より早く折り返したいのだ。だが px のブレークポイントはテキストが大きくなったことを知らない。依然としてビューポート幅ちょうど 600px で切り替わるため、レイアウトは誤ったタイミングで切り替わり、コンテンツが窮屈になったり重なったりする。
二つのアプローチを比べてみよう:
/* px のブレークポイント — ユーザーのフォントサイズ設定を無視 */
@media (min-width: 600px) {
.sidebar { display: block; }
}
/* em/rem のブレークポイント — ユーザーのフォントサイズに反応 */
@media (min-width: 37.5em) {
.sidebar { display: block; }
}
37.5em はデフォルトの 16px で 600px だ(600 ÷ 16 = 37.5)。違いは挙動にある。ユーザーがデフォルトのフォントサイズを倍にすると、em のブレークポイントも実質的に倍になるため、レイアウトはテキストに比例したビューポート幅で切り替わる。まさにコンテンツがそれを必要とするときにだ。px のブレークポイントは凍りついたままだ。
知っておく価値のある一つの癖:メディアクエリの条件部では、em も rem も html の上書きではなくブラウザのデフォルトフォントサイズを基準に解決されるため、そこでは両者は同一に振る舞う。どちらの単位でもバグは直る。px がそれを引き起こす。
プロパティ別の決定表
迷ったときは、この表が答えをくれる。毎回ロジックを導き直さずに判断する最速の方法だ。
| プロパティ | 推奨単位 | 理由 |
|---|---|---|
font-size | rem | ユーザーのフォントサイズ設定に合わせて拡大縮小する |
padding / margin | rem | 間隔がテキストと一緒に拡大縮小する |
border | px | ヘアラインはくっきり固定であるべき |
box-shadow のオフセット | px | コンテンツではなく正確なレンダリング詳細 |
border-radius | rem | 角の丸みをスケールに比例して保つ |
| メディアクエリ | em / rem | ブレークポイントはフォントサイズズームに反応すべき |
width / max-width | rem(テキストには ch が多い) | 拡大縮小可能なレイアウト幅。ch は行長を制限 |
line-height | 単位なし | 単位なしの乗数は正しく継承される |
line-height の行は注記に値する。よくあるバグだからだ。常に line-height: 1.5 と、単位なしで書くこと。単位なしの値は各要素が自身のフォントサイズに対して計算する乗数なので、入れ子の要素も読みやすさを保つ。代わりに line-height: 1.5em や 24px と書くと、計算済みの長さが継承されてしまう。つまり大きいフォントサイズの子が親の行の高さを引き継ぎ、テキストが衝突し始める。単位なしならこの問題ごと回避できる。
px と rem の換算
基準さえ押さえれば、算数は頭の中でできるほど小さい:16px = 1rem。rem にするには 16 で割り、px に戻すには 16 を掛ける。
| px | rem(16px ベース) |
|---|---|
| 8px | 0.5rem |
| 12px | 0.75rem |
| 16px | 1rem |
| 24px | 1.5rem |
| 32px | 2rem |
62.5% のテクニックを使うなら、ベースは 10px になり算数はさらに簡単になる。10 で割るか掛けるだけだ。24px = 2.4rem。唯一のルールは、スタイルシートが実際に設定するベースを基準に常に換算することだ。
それ以外のすべて — 半端な値、カスタムのルート、Figma エクスポートの一括変換 — については、頭の中の計算をやめてpx → rem 変換ツールかrem → px 変換ツールを使おう。どちらも任意のルートフォントサイズを設定でき、どちらの方向にもリアルタイムで変換する。そして後で単位が混在したスタイルシートを整えるなら、CSS フォーマッターが間隔とインデントを整えてくれる。
よくある間違い
単位まわりの厄介ごとの大半は、いくつかのパターンが原因だ。
ルートのフォントサイズを px で設定する。 html { font-size: 16px } と書くと(デフォルトのままにするか 100% / パーセンテージを使うのではなく)、ユーザーのブラウザのフォントサイズ設定をまるごと上書きしてしまう。rem 値は依然としてそれを基準に計算されるが、読者はもうページ全体を拡大縮小できない。ルートはデフォルトのままにするか、パーセンテージを使うこと。
px と rem を体系なく混ぜる。 あるフォントサイズは px、あるものは rem、間隔は両者に分かれる。結果はユーザーがテキストを調整したときに不均一に拡大縮小するレイアウトだ。rem をデフォルトに選び、px は決定表にある意図的な例外に取っておくこと。
グローバルな間隔に em を使う。 広く入れ子になったコンテナで em を使うと複合の罠が再来し、ツリーの深いところの padding が誰も意図しないものに解決される。グローバルな間隔は rem に保ち、em は局所的でコンポーネントスコープの値に取っておくこと。
line-height に単位を付ける。 前述のとおり、line-height: 24px や 1.5em は計算済みの長さとして継承され、異なるフォントサイズの要素で壊れる。常に単位なしの乗数を使うこと。
FAQ
rem は px より優れているか?
ほとんどのサイズ指定については、そうだ。rem はユーザーのブラウザのフォントサイズ設定に合わせて拡大縮小するが、px はそれを無視するからだ。ただし「優れている」はプロパティ次第だ。1px のボーダーやシャドウのオフセットのようにくっきり保つべき固定の詳細には px が正しい選択である。コンテンツのサイズ指定には rem、レンダリングの詳細には px を使う。
1rem は何ピクセルか?
1rem はルートのフォントサイズのピクセル値に等しく、ほぼすべてのブラウザでデフォルトは 16px だ。したがって標準的な環境では 1rem = 16px、1.5rem = 24px、2rem = 32px となる。スタイルシートが html { font-size } を上書きしていれば — たとえば 62.5% のテクニックで 10px にするなど — 1rem はその値に等しくなる。
font-size には rem と em のどちらを使うべきか?
font-size にはほぼすべての場合で rem を使う。rem はルートを基準に測るため、要素がどれだけ深く入れ子になっても予測可能なままだ。em は親のフォントサイズを基準に測るので、ツリーを下るにつれて複合し、入れ子のテキストが予想外に膨れ上がる。em は単一のコンポーネントに紐づく局所値に取っておくこと。
rem ではなく px を使うべきはいつか?
ユーザーのフォントサイズに合わせて拡大縮小すべきでない値に px を使う。1px のボーダー、正確な box-shadow のオフセット、フォーカスリングのアウトライン、その他の固定のレンダリング詳細だ。これらはコンテンツではなくくっきりしたデザインの詳細なので、px で固定するほうがアクセシブルな選択になる。コンテンツに関わるものはすべて rem を使うべきだ。
px を使うとなぜメディアクエリが壊れるのか?
px のメディアクエリのブレークポイントはブラウザのフォントサイズズームに反応しない。ユーザーがデフォルトのフォントを拡大すると、コンテンツはより多くの幅を必要とするが、px のブレークポイントは依然として同じビューポート幅で切り替わる。だからレイアウトは誤ったタイミングで切り替わる。ユーザーのフォントサイズに合わせて拡大縮小する em か rem のブレークポイントを使うこと。
62.5% のフォントサイズのテクニックとは?
62.5% のテクニックは html { font-size: 62.5% } を設定し、ルートのフォントサイズを 10px(16 の 62.5%)にする。10px ベースなら rem の算数は「10 で割る」になる。24px = 2.4rem、12px = 1.2rem。そのうえで開発者は body { font-size: 1.6rem } を設定し、読みやすい 16px の本文テキストに戻す。
px、rem、em を混ぜてもよいか?
よい。px、rem、em を混ぜるのは、それぞれが適したプロパティに従う限り正しい。タイポグラフィと間隔には rem、固定の詳細には px、局所的でコンポーネントスコープの値には em だ。厄介ごとを招くのは体系なく混ぜること、たとえばあるフォントサイズは px であるものは rem、というやり方だ。rem をデフォルトに選び、px と em は意図的な例外として扱うこと。
padding と margin にはどの単位を使うべきか?
padding と margin には rem を使い、ユーザーがフォントサイズを調整したときに間隔がテキストと一緒に拡大縮小するようにする。これがレイアウトを比例的でアクセシブルに保つ。ボタンの padding がテキストと一緒に大きくなるような、要素自身のフォントサイズに連動すべき padding には em を取っておき、複合する深い入れ子のコンテナでは em を避けること。