CSSの主要単位は px(固定値)、rem(ルート基準の相対値)、em(親要素基準の相対値)、%(親要素の幅/高さ比)、vw/vh(ビューポート基準)の5種。font-sizeにはrem、コンポーネント内paddingにはem、レイアウト幅には%、全画面表示にはvw/vhが基本。clamp(min, preferred, max)を使えばメディアクエリなしで流体サイズを実現できる。
CSSの単位を理解する
CSSで使える長さの単位は大きく絶対単位と相対単位に分かれる。絶対単位はデバイスに関係なく常に同じサイズを表し、相対単位は基準となる値に応じて伸縮する。Web制作で実際に使うのは以下の6種+1関数がほぼ全てだ。
| 単位 | 種別 | 基準 | 代表的な用途 |
|---|---|---|---|
px |
絶対 | CSSピクセル(1/96インチ) | border, box-shadow, 細かい装飾 |
rem |
相対 | ルート要素(html)のfont-size | font-size, margin, padding |
em |
相対 | 親要素のfont-size | コンポーネント内の余白・アイコンサイズ |
% |
相対 | 親要素のサイズ | レイアウト幅, max-width |
vw |
相対 | ビューポートの幅(1vw = 1%) | 全幅セクション, ヒーローエリア |
vh |
相対 | ビューポートの高さ(1vh = 1%) | ファーストビュー, フルスクリーン |
clamp() |
関数 | 最小値・推奨値・最大値の3値 | 流体タイポグラフィ, レスポンシブ余白 |
ここからは各単位の特徴・適切な使い所・避けるべきケースを具体的なコードとともに見ていく。
px — いつ使うべきか
pxはCSSの絶対単位で、1pxは1/96インチ(CSSピクセル)に相当する。Retinaディスプレイでは1CSSピクセルが複数の物理ピクセルに対応するが、ブラウザがスケーリングを処理するためコード上は気にしなくていい。
pxを使うべき場面
pxが最適なのは「サイズが変わるべきではない」要素だ。ユーザーがブラウザのフォントサイズ設定を変更しても、ボーダーの太さや影の大きさまで比例して拡大してほしくはない。
/* pxが適切なケース */
/* 1. ボーダー: 1pxの線は1pxのまま表示したい */
.card {
border: 1px solid #e2e8f0;
border-radius: 8px;
}
/* 2. box-shadow: 影の広がりは固定が自然 */
.card:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* 3. outline: フォーカスリングは常に同じ太さ */
.button:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
/* 4. メディアクエリのブレイクポイント */
@media (max-width: 768px) {
/* 768pxはビューポートの実サイズ基準 */
}
pxを避けるべき場面
font-sizeにpxを使うとアクセシビリティ上の問題が生じる。ブラウザの「フォントサイズ設定」はルート要素のfont-sizeを変更する仕組みだが、pxで指定されたテキストはこの設定に反応しない。視覚障害のあるユーザーがフォントサイズを「大」に設定しても文字が大きくならないのだ。
/* NG: font-sizeにpx */
.heading {
font-size: 24px; /* ブラウザ設定で拡大されない */
}
/* OK: font-sizeにrem */
.heading {
font-size: 1.5rem; /* ブラウザ設定に連動して拡大される */
}
Point
WCAG 2.2の達成基準1.4.4(テキストのサイズ変更)では、テキストを200%まで拡大してもコンテンツや機能が損なわれないことが求められている。font-sizeにpxを使うとこの基準を満たせない可能性がある。font-sizeには必ずremを使うのが現代の鉄則だ。
rem — フォントサイズと余白の基準
rem(root em)はルート要素(html)のfont-sizeを基準とする相対単位だ。ほとんどのブラウザでデフォルトのルートfont-sizeは16pxなので、1rem = 16px が基本の換算になる。
/* ブラウザのデフォルト: html { font-size: 16px; } */
.text {
font-size: 1rem; /* = 16px */
font-size: 1.25rem; /* = 20px */
font-size: 0.875rem; /* = 14px */
font-size: 2rem; /* = 32px */
}
/* margin/paddingにもremが有効 */
.section {
margin-bottom: 2rem; /* = 32px, フォントサイズに比例 */
padding: 1.5rem; /* = 24px */
}
62.5%テクニック(非推奨)
古い記事で見かける html { font-size: 62.5%; }(1rem = 10pxにする方法)は、計算が楽になる反面、サードパーティのライブラリやブラウザのデフォルトスタイルが壊れるリスクがある。現在はブラウザの開発者ツールで換算を確認できるため、62.5%テクニックを使う必要はない。
/* 非推奨: 62.5%テクニック */
html {
font-size: 62.5%; /* 1rem = 10px */
}
body {
font-size: 1.6rem; /* 全体を16pxに戻す必要がある */
}
/* サードパーティCSSが10pxベースで崩れる可能性あり */
/* 推奨: デフォルトの16pxベースをそのまま使う */
html {
font-size: 100%; /* 1rem = 16px (デフォルトのまま) */
}
.heading {
font-size: 1.5rem; /* = 24px */
}
.small-text {
font-size: 0.875rem; /* = 14px */
}
remとpxの早見表
| rem | px(16px基準) | よくある用途 |
|---|---|---|
0.75rem |
12px | キャプション、注釈 |
0.875rem |
14px | 小さめの本文、ラベル |
1rem |
16px | 本文テキスト |
1.125rem |
18px | リード文 |
1.25rem |
20px | h4相当 |
1.5rem |
24px | h3相当 |
2rem |
32px | h2相当 |
2.5rem |
40px | h1相当 |
em — コンポーネント内の相対指定
emはその要素自身の(font-sizeプロパティ以外)または親要素の(font-sizeプロパティの場合)font-sizeを基準とする相対単位だ。remと名前が似ているが、基準が異なるため使い所がまったく違う。
/* emの基準の違い */
.parent {
font-size: 20px;
}
.parent .child {
font-size: 0.8em; /* 親の20px × 0.8 = 16px */
padding: 1em; /* 自身の16px × 1 = 16px */
margin-bottom: 1em; /* 自身の16px × 1 = 16px */
}
emの強み: スケーラブルなコンポーネント
emの真価は「font-sizeを変えるだけでコンポーネント全体がスケールする」ことだ。ボタンの余白やアイコンサイズをemで指定しておけば、サイズバリエーション(small / medium / large)をfont-size一行で切り替えられる。
/* emでスケーラブルなボタン */
.btn {
font-size: 1rem;
padding: 0.5em 1.2em; /* font-sizeに比例して伸縮 */
border-radius: 0.3em; /* 角丸もfont-sizeに比例 */
gap: 0.4em; /* アイコンとテキストの間隔 */
}
.btn .icon {
width: 1em; /* テキストと同じ高さ */
height: 1em;
}
/* font-sizeを変えるだけでサイズバリエーション完成 */
.btn_size_small { font-size: 0.75rem; } /* 全体が75%に */
.btn_size_medium { font-size: 1rem; } /* 基準サイズ */
.btn_size_large { font-size: 1.25rem; } /* 全体が125%に */
emの注意点: 入れ子の罠
emは累積する。font-size: 0.9emを指定したリストの中にさらにリストがあると、孫要素は0.9 × 0.9 = 0.81倍、ひ孫は0.9 × 0.9 × 0.9 = 0.729倍と、どんどん小さくなる。font-sizeの指定にはemではなくremを使うのが安全だ。
/* NG: font-sizeにem → 入れ子で累積する */
.list {
font-size: 0.9em;
}
/* .list > .list → 0.81em、.list > .list > .list → 0.729em */
/* OK: font-sizeにrem → 常にルート基準 */
.list {
font-size: 0.9rem;
}
/* どれだけ入れ子にしても0.9rem(=14.4px)のまま */
Point
emとremの使い分けは明快だ。font-sizeにはrem(ルート基準で予測しやすい)、padding・margin・border-radiusにはem(font-sizeに連動してスケール)。この2つを守れば大半のケースで困らない。
vw / vh — ビューポート基準の単位
vw(viewport width)とvh(viewport height)はブラウザの表示領域の幅・高さを基準とする単位だ。1vw = ビューポート幅の1%、1vh = ビューポート高さの1% になる。
/* ファーストビューのヒーローセクション */
.hero {
height: 100vh; /* ビューポートの高さいっぱい */
width: 100vw; /* ビューポートの幅いっぱい */
}
/* 画面幅に応じたフォントサイズ */
.hero__title {
font-size: 5vw; /* 画面幅1200px → 60px, 375px → 18.75px */
}
vwをfont-sizeに使うリスク
font-size: 5vw のように指定すると、画面幅に完全に比例してフォントサイズが変わる。一見便利だが、超大画面では巨大に、超小画面では極小になり制御が効かない。さらに、ブラウザのフォントサイズ設定が無効化されるためアクセシビリティの問題も生じる。
/* NG: font-sizeに単独でvw */
.title {
font-size: 5vw;
/* 4Kモニター(3840px): 192px — 巨大すぎる */
/* スマホ(375px): 18.75px — 小さすぎる場合がある */
/* ブラウザ設定のフォントサイズ拡大が効かない */
}
/* OK: clamp()で上限・下限を設ける */
.title {
font-size: clamp(1.5rem, 4vw + 0.5rem, 3.5rem);
/* 最小24px、最大56px の範囲で流体変化 */
/* rem混在でブラウザ設定にも部分的に対応 */
}
100vhの罠(モバイルブラウザ)
モバイルブラウザでは、アドレスバーの表示/非表示で実際のビューポート高さが変動する。100vh はアドレスバーを含まない「大きいビューポート」を参照するため、アドレスバー表示時にコンテンツがはみ出す問題が長年あった。2023年以降、この問題を解決する新しい単位が追加されている。
| 単位 | 説明 | サポート状況 |
|---|---|---|
svh |
Small Viewport Height(アドレスバー表示時の高さ) | Chrome 108+ / Safari 15.4+ |
lvh |
Large Viewport Height(アドレスバー非表示時の高さ) | Chrome 108+ / Safari 15.4+ |
dvh |
Dynamic Viewport Height(リアルタイムで変動) | Chrome 108+ / Safari 15.4+ |
/* モバイル対応のフルスクリーン */
/* 従来の方法(アドレスバーで崩れる可能性) */
.fullscreen {
height: 100vh;
}
/* 推奨: dvhを使い、100vhをフォールバックに */
.fullscreen {
height: 100vh; /* フォールバック */
height: 100dvh; /* 対応ブラウザではこちらが優先 */
}
/* ファーストビューはsvhが安全(最小の高さで確保) */
.hero {
min-height: 100vh;
min-height: 100svh;
}
% — 親要素に対する相対値
% は親要素のサイズを基準とする相対単位だ。ただし、何に対する%なのかはプロパティによって変わる——ここが%の最大の落とし穴になる。
| プロパティ | 基準 | 例 |
|---|---|---|
width |
親要素の幅 | width: 50% → 親の幅の半分 |
height |
親要素の高さ | 親にheightが明示されていないと効かない |
padding |
親要素の幅(上下paddingも幅が基準) | padding-top: 56.25% → 16:9アスペクト比 |
margin |
親要素の幅(上下marginも幅が基準) | margin: 0 auto → 中央寄せ |
font-size |
親要素のfont-size | emと同じ動作(累積する) |
transform: translate |
自分自身のサイズ | translateX(-50%) → 自身の幅の半分左に |
/* %の実践的な使い方 */
/* レイアウト: max-widthで最大幅を制限 */
.container {
width: 90%; /* 親の90%を使う */
max-width: 1200px; /* 最大1200pxで止める */
margin: 0 auto; /* 中央寄せ */
}
/* 2カラムレイアウト */
.sidebar {
width: 30%;
}
.main {
width: 70%;
}
/* アスペクト比の維持(従来の方法) */
.video-wrapper {
position: relative;
padding-top: 56.25%; /* 16:9 = 9/16 × 100 */
}
.video-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
/* 推奨: aspect-ratioプロパティ(モダン) */
.video-wrapper {
aspect-ratio: 16 / 9;
}
Point
padding-top や padding-bottom に % を指定すると、基準は親の幅(高さではない)になる。これは仕様通りの動作で、かつてはアスペクト比の維持に利用されていた。現在は aspect-ratio プロパティが全モダンブラウザでサポートされているため、そちらを推奨する。
clamp() — 流体タイポグラフィの実装
clamp(最小値, 推奨値, 最大値)は3つの値を取るCSS関数で、推奨値が最小値〜最大値の範囲に収まるよう制約する。メディアクエリを書かずにビューポート幅に応じてフォントサイズや余白を流体的に変化させるために使う。2026年現在、全モダンブラウザ(対応率96%超)でサポート済みだ。
/* clamp()の基本構文 */
font-size: clamp(最小値, 推奨値, 最大値);
/* 例: 見出しのfont-size */
h1 {
font-size: clamp(1.5rem, 2.5vw + 1rem, 3rem);
/*
* 最小: 1.5rem (24px) — スマホでこれ以下にならない
* 推奨: 2.5vw + 1rem — ビューポート幅に応じて滑らかに変化
* 最大: 3rem (48px) — デスクトップでこれ以上にならない
*/
}
推奨値の計算方法
推奨値(preferred value)には vw + rem の形式を使うのがベストプラクティスだ。vw単独だとブラウザのフォントサイズ設定が無効化されるが、remを加算すると設定に部分的に反応する。
/* 推奨値の設計パターン */
/* パターン1: 本文テキスト(控えめな流体変化) */
body {
font-size: clamp(1rem, 0.5vw + 0.875rem, 1.125rem);
/* 16px → 18px の間で緩やかに変化 */
}
/* パターン2: セクション見出し(中程度の流体変化) */
h2 {
font-size: clamp(1.25rem, 2vw + 0.5rem, 2rem);
/* 20px → 32px の間で変化 */
}
/* パターン3: ヒーロー見出し(大胆な流体変化) */
.hero-title {
font-size: clamp(2rem, 5vw + 0.5rem, 4.5rem);
/* 32px → 72px の間で変化 */
}
/* パターン4: セクション間の余白 */
.section {
padding: clamp(2rem, 5vw, 6rem) 0;
/* 32px → 96px の間で変化 */
}
clamp()でメディアクエリを削減する
従来のレスポンシブ対応では、ブレイクポイントごとにfont-sizeやpaddingを切り替える必要があった。clamp()を使うと1行で済み、しかもブレイクポイント間の滑らかな変化が得られる。
/* 従来: メディアクエリで段階的に切り替え */
h1 { font-size: 1.5rem; }
@media (min-width: 600px) {
h1 { font-size: 2rem; }
}
@media (min-width: 1024px) {
h1 { font-size: 2.5rem; }
}
@media (min-width: 1440px) {
h1 { font-size: 3rem; }
}
/* clamp(): 1行で滑らかに変化 */
h1 {
font-size: clamp(1.5rem, 2.5vw + 1rem, 3rem);
}
ツールで効率化
clamp()の値を手計算するのは面倒だ。当サイトのCSS clamp()ジェネレーターなら、最小・最大のfont-sizeとビューポート幅を入力するだけで最適なclamp()式を自動生成できる。登録不要・完全無料。
場面別の使い分け早見表
最後に、プロパティ×単位の組み合わせを早見表にまとめた。迷ったらこの表を参照してほしい。
| プロパティ | 推奨単位 | 理由 |
|---|---|---|
font-size |
rem / clamp() |
アクセシビリティ(ブラウザ設定に連動)+ レスポンシブ |
line-height |
単位なし(1.5等) |
font-sizeの倍率として動作。emや%だと入れ子で崩れやすい |
padding / margin |
rem / em |
remで全体統一、emでコンポーネント内スケール |
width(レイアウト) |
% / fr |
親要素やグリッドコンテナーに対する比率 |
max-width |
px / rem |
「この幅を超えない」という絶対的な上限 |
height(フルスクリーン) |
dvh / svh |
モバイルのアドレスバー問題を回避 |
border |
px |
線の太さはフォント拡大で変わるべきではない |
border-radius |
px / em |
固定ならpx、コンポーネントと連動させるならem |
box-shadow |
px |
影の広がりは固定サイズが自然 |
gap(Grid/Flex) |
rem / clamp() |
全体の余白設計と統一。レスポンシブならclamp() |
| メディアクエリ | px |
ビューポートの物理サイズに対する閾値 |
Point
line-heightは単位なしの数値(例: 1.5)で指定するのがベストプラクティスだ。line-height: 1.5em や line-height: 150% は計算値が固定されて子要素に継承されるため、font-sizeが異なる子要素で行間が崩れる。単位なしなら常にfont-sizeの倍率として再計算される。