WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

clamp()でレスポンシブフォント|文字サイズをメディアクエリなしで可変に

生徒

画面幅ごとに文字サイズを変えたくて、メディアクエリが5個くらいに増えてしまいました…。

ペン博士

それclamp()なら1行で済むよ。最小・推奨・最大の3つを渡すだけ。今日は計算の考え方と、見出し・本文・余白まで使える値をまとめて渡すね。

見出しはPCで48px、スマホで28px。この調整をメディアクエリで書くと、ブレイクポイントの数だけ指定が増えていきます。しかも境界で文字サイズがカクッと飛ぶのが避けられません。

clamp()を使えば、画面幅に応じてなめらかに変化する文字サイズを1行で書けます。しかも下限と上限を自分で決められるので、小さくなりすぎる・大きくなりすぎるという事故も起きません。

この記事では、3つの引数の意味から、実務で使える計算方法、見出し・本文・余白への適用、アクセシビリティ上の注意点まで解説します。コピペで使える値の一覧も用意しました。

  • clamp()の3つの引数(最小・推奨・最大)
  • vwだけでは破綻する理由
  • 推奨値の計算方法(2点を通る直線の求め方)
  • 見出し・本文・余白のコピペ値一覧
  • メディアクエリとの使い分け
  • min()・max()との違い
  • 文字の拡大設定を壊さない書き方
  • CSS変数と組み合わせた型の作り方
  • 効かないときの原因と対処
  • 実務で使うタイプスケール一式

目次

clamp()の3つの引数

3つの引数の役割

書き方は単純です。カンマ区切りで3つの値を渡します。

/* clamp(最小値, 推奨値, 最大値) */
h1 {
  font-size: clamp(1.75rem, 5vw, 3rem);
}
位置役割この例では
第1引数これ以上小さくしない下限1.75rem(28px)
第2引数画面幅に応じて変わる推奨値5vw
第3引数これ以上大きくしない上限3rem(48px)

動きはこうです。画面が狭いときは5vwが小さくなるので下限の28pxで止まる。広いときは5vwが大きくなるので上限の48pxで止まる。その中間だけが可変になります。

つまり「小さい画面でも読める大きさは確保しつつ、大画面で巨大化もしない」という制御が1行で書けます。

vwだけでは破綻する理由

vw単独とclamp()

可変にするだけならfont-size: 5vwでも動きます。しかし実用に耐えません。

/* 悪い例:下限も上限もない */
h1 {
  font-size: 5vw;
}

/* 320pxのスマホ →  16px(見出しなのに本文と同じ)
   1920pxのモニタ → 96px(画面を埋め尽くす) */

さらに深刻なのが拡大表示への影響です。vwは画面幅だけで決まるため、利用者がブラウザの文字サイズを大きくしてもまったく変化しません

clamp()の下限・上限にremを使えば、拡大設定が効く範囲を残せます。これが実務でclamp()を選ぶ最大の理由です。

推奨値の計算方法

推奨値の求め方

第2引数は勘で決めても動きますが、狙った画面幅で狙ったサイズにしたいときは計算します。2点を通る直線を求めるだけです。

考え方

  • 例:画面幅375pxのとき28px、1200pxのとき48pxにしたい
  • 傾き = (48 − 28) ÷ (1200 − 375) = 0.02424
  • vw換算 = 0.02424 × 100 = 2.42vw
  • 切片 = 28 − (0.02424 × 375) = 18.91px = 1.18rem
/* 375px→28px、1200px→48px をなめらかにつなぐ */
h1 {
  font-size: clamp(1.75rem, 1.18rem + 2.42vw, 3rem);
}

第2引数がrem + vwの形になっているのがポイントです。remの部分が拡大設定に反応するため、vw単独より安全になります。

計算を省略したいとき

毎回計算するのが面倒なら、次の早見表の値をそのまま使ってください。よく使う範囲は網羅しています。

用途clamp()の値375px→1200pxでの変化
特大見出しclamp(2rem, 1.2rem + 3.4vw, 3.5rem)32px → 56px
h1clamp(1.75rem, 1.18rem + 2.42vw, 3rem)28px → 48px
h2clamp(1.4rem, 1.09rem + 1.33vw, 2rem)22px → 32px
h3clamp(1.15rem, 1rem + 0.6vw, 1.5rem)18px → 24px
本文clamp(1rem, 0.95rem + 0.2vw, 1.125rem)16px → 18px
注釈clamp(0.8rem, 0.77rem + 0.12vw, 0.875rem)13px → 14px

見出し・本文・余白への適用

clamp()は文字サイズ専用ではありません。数値を取るプロパティならどこでも使えます。

/* 余白:狭い画面で詰まりすぎない */
.section {
  padding-block: clamp(2.5rem, 1.5rem + 4vw, 6rem);
}

/* 記事の横幅:小さい画面では全幅、大画面では読みやすい幅で止める */
.container {
  width: clamp(320px, 92vw, 760px);
  margin-inline: auto;
}

/* 要素同士の間隔 */
.cards {
  gap: clamp(1rem, 0.5rem + 2vw, 2rem);
}

/* 角丸やボーダーにも使える */
.hero {
  border-radius: clamp(8px, 1.5vw, 24px);
}

特に効果が大きいのはセクションの上下余白です。PCで十分な余白を取ると、スマホでは間延びします。clamp()にしておけば、両方で適切な間隔になります。

本文にclamp()を使うときの注意

本文の文字サイズにも使えますが、見出しほど大胆に変えてはいけません。

/* 良い例:変化幅は2px程度に留める */
body {
  font-size: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
  line-height: 1.8;
}

/* 悪い例:本文が画面幅で大きく動く */
body {
  font-size: clamp(0.875rem, 3vw, 1.5rem);  /* 読みにくい */
}

本文は16〜18pxの範囲で十分です。読者は文字サイズの変化に敏感で、大きく動くと落ち着いて読めません。可変にするのは見出しと余白、と割り切るのが実務的です。

行間も合わせて調整すると質が上がります。文字が大きくなるほど行間の倍率は下げるのが定石です。

h1 {
  font-size: clamp(1.75rem, 1.18rem + 2.42vw, 3rem);
  line-height: 1.25;    /* 大きい文字は詰め気味に */
}

p {
  font-size: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
  line-height: 1.8;     /* 本文はゆったり */
}

メディアクエリとの使い分け

clamp()があればメディアクエリが不要になるわけではありません。役割が違います。

やりたいこと適した方法理由
文字サイズを滑らかに変えるclamp()境界で飛ばない
余白を画面幅に合わせるclamp()1行で書ける
1カラム↔2カラムの切替メディアクエリレイアウトは段階的に変わる
要素の表示・非表示メディアクエリ連続値では表せない
フォント自体を変えるメディアクエリ値ではなく種類の切替

目安は連続的に変わるものはclamp()、段階的に切り替わるものはメディアクエリです。この線引きで書くと、CSSの見通しが良くなります。

min()・max()との違い

同じ系統の関数が3つあります。clamp()は残り2つの組み合わせです。

/* min():候補のうち小さいほうを採用=上限を設ける */
.box { width: min(90vw, 800px); }

/* max():候補のうち大きいほうを採用=下限を設ける */
.title { font-size: max(1.25rem, 3vw); }

/* clamp(a, b, c) は max(a, min(b, c)) と同じ意味 */
.title { font-size: clamp(1.25rem, 3vw, 2.5rem); }

上限だけ、下限だけで足りる場面ではmin()やmax()のほうが読みやすくなります。両方必要になったらclamp()にする、という順で考えます。

文字の拡大設定を壊さない書き方

見落とされがちですが、アクセシビリティ上の重要な論点です。ブラウザの文字サイズ変更で本文が拡大できないサイトは、読めない人が出ます。

  • vw単独は使わない:拡大設定にまったく反応しない
  • 下限・上限はremで書く:ルートの文字サイズに追従する
  • 第2引数にもremを混ぜるrem + vwの形にする
  • 本文の下限は1rem以上:16pxを下回らせない
/* 拡大設定が効く書き方 */
h1 { font-size: clamp(1.75rem, 1.18rem + 2.42vw, 3rem); }

/* 効かない書き方(避ける) */
h1 { font-size: clamp(28px, 5vw, 48px); }

pxで固定した場合、ブラウザの文字サイズ設定を変えても見た目が1ミリも変わりません。remで書くだけで対応できるので、初期段階からremに統一しておくのが安全です。

CSS変数と組み合わせて型にする

効かない時の原因

プロジェクト全体で使うなら、変数にまとめておくと管理が一箇所で済みます。

:root {
  --fs-xs: clamp(0.8rem, 0.77rem + 0.12vw, 0.875rem);
  --fs-base: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
  --fs-lg: clamp(1.15rem, 1rem + 0.6vw, 1.5rem);
  --fs-xl: clamp(1.4rem, 1.09rem + 1.33vw, 2rem);
  --fs-2xl: clamp(1.75rem, 1.18rem + 2.42vw, 3rem);

  --space-sm: clamp(0.75rem, 0.6rem + 0.6vw, 1.25rem);
  --space-md: clamp(1.5rem, 1.1rem + 1.6vw, 3rem);
  --space-lg: clamp(2.5rem, 1.5rem + 4vw, 6rem);
}

h1 { font-size: var(--fs-2xl); }
h2 { font-size: var(--fs-xl); }
h3 { font-size: var(--fs-lg); }
body { font-size: var(--fs-base); }
.section { padding-block: var(--space-lg); }

この6行を用意しておけば、以降はサイズを直接書かずに済みます。デザインの調整も変数の値を変えるだけで全体に反映されます。

効かないときの原因と対処

clamp()が思ったように動かない場合、原因はほぼ次の5つです。

症状原因対処
まったく効かない引数が2つ以下3つ渡す(min/maxと混同)
常に最小値になる推奨値が小さすぎるvwの係数を上げる
常に最大値になる推奨値が大きすぎるvwの係数を下げる
文字拡大に反応しないpx指定になっているremに置き換える
横スクロールが出る幅にvwを使った%min()にする

最後の項目は見落としやすいポイントです。100vwスクロールバーの幅を含むため、幅の指定に使うと横スクロールが発生します。幅には%を使ってください。

実務で使うタイプスケール一式

コピペしてそのまま使える一式です。375px〜1200pxの範囲でなめらかに変化します。

:root {
  /* 文字サイズ */
  --fs-note: clamp(0.8rem, 0.77rem + 0.12vw, 0.875rem);
  --fs-body: clamp(1rem, 0.95rem + 0.2vw, 1.125rem);
  --fs-h3: clamp(1.15rem, 1rem + 0.6vw, 1.5rem);
  --fs-h2: clamp(1.4rem, 1.09rem + 1.33vw, 2rem);
  --fs-h1: clamp(1.75rem, 1.18rem + 2.42vw, 3rem);
  --fs-hero: clamp(2rem, 1.2rem + 3.4vw, 3.5rem);

  /* 余白 */
  --sp-xs: clamp(0.5rem, 0.45rem + 0.2vw, 0.75rem);
  --sp-sm: clamp(0.75rem, 0.6rem + 0.6vw, 1.25rem);
  --sp-md: clamp(1.5rem, 1.1rem + 1.6vw, 3rem);
  --sp-lg: clamp(2.5rem, 1.5rem + 4vw, 6rem);
}

body {
  font-size: var(--fs-body);
  line-height: 1.8;
}

h1 { font-size: var(--fs-h1); line-height: 1.25; }
h2 { font-size: var(--fs-h2); line-height: 1.35; }
h3 { font-size: var(--fs-h3); line-height: 1.45; }

.section { padding-block: var(--sp-lg); }
.container { width: min(92%, 760px); margin-inline: auto; }
.stack > * + * { margin-top: var(--sp-md); }

これだけでメディアクエリを1つも書かずに、文字サイズと余白のレスポンシブ対応が完了します。レイアウトの切り替えが必要になった時点で、初めてメディアクエリを足してください。

実例で見る導入前後の違い

実際のパーツで、メディアクエリ版とclamp()版を比べます。行数と保守性の差が分かります。

ヒーロー見出しの場合

/* メディアクエリ版:4段階でも境界で飛ぶ */
.hero__title { font-size: 1.75rem; }

@media (min-width: 480px)  { .hero__title { font-size: 2.25rem; } }
@media (min-width: 768px)  { .hero__title { font-size: 2.75rem; } }
@media (min-width: 1024px) { .hero__title { font-size: 3.25rem; } }
@media (min-width: 1280px) { .hero__title { font-size: 3.5rem; } }

/* clamp()版:1行、しかも境界が無い */
.hero__title { font-size: clamp(1.75rem, 1.2rem + 3.4vw, 3.5rem); }

13行が1行になり、さらに479pxと480pxの間で文字が飛ぶ現象も消えます。ブレイクポイントを増やすほど差が開きます。

セクション余白の場合

/* メディアクエリ版 */
.section { padding-block: 2.5rem; }
@media (min-width: 768px)  { .section { padding-block: 4rem; } }
@media (min-width: 1200px) { .section { padding-block: 6rem; } }

/* clamp()版 */
.section { padding-block: clamp(2.5rem, 1.5rem + 4vw, 6rem); }

余白は文字サイズ以上に段階が目立ちます。スクロール中に急に間隔が変わると、レイアウトが崩れたように見えることがあります。

カードの内側余白と角丸

.card {
  padding: clamp(1rem, 0.7rem + 1.2vw, 2rem);
  border-radius: clamp(10px, 0.6vw + 8px, 16px);
  gap: clamp(0.75rem, 0.5rem + 1vw, 1.5rem);
}

小さな画面では余白を詰めて情報量を確保し、大きな画面ではゆったり見せる。カード内部の3つの値を同時に可変にすると、密度が自然に変わります。

コンテナクエリとの関係

clamp()は画面幅を基準にします。一方、カードのように置き場所によって幅が変わる部品では、親要素の幅を基準にしたいことがあります。

/* 親をコンテナとして登録する */
.card-area {
  container-type: inline-size;
}

/* 親の幅に応じて可変にする(cqi=コンテナ幅の1%) */
.card__title {
  font-size: clamp(1rem, 0.85rem + 2cqi, 1.5rem);
}

vwcqiに置き換えるだけで、画面幅ではなく親要素の幅に反応するようになります。サイドバーとメインで同じカードを使い回す場合に効果的です。

単位基準使う場面
vw画面の幅ページ全体の見出し・余白
cqiコンテナの幅どこにでも置く部品
%親要素の幅レイアウトの幅指定
remルートの文字サイズ拡大設定に追従させたい値

すべてclamp()の中で混ぜて使えます。基準をどこに置くかだけ意識してください。

導入手順とチェック

既存サイトに入れる場合は、影響範囲の小さい順に進めるのが安全です。

  • ①変数を定義する:まず:rootにサイズと余白の変数を置く
  • ②見出しから置き換える:影響が見た目に限られ、確認しやすい
  • ③セクション余白:縦のリズムが整う。ここで効果を実感できる
  • ④本文:変化幅を小さく。最後に慎重に触る
  • ⑤不要になったメディアクエリを削除:置き換え漏れが無いか確認してから
/* 確認用:現在の計算値をDevToolsで見る
   要素を選択 → Computed → font-size の値が
   画面幅を変えるたびに滑らかに変わればOK */

検証は375px・768px・1200px・1920pxの4点で行えば十分です。下限と上限で止まっているか、中間でなめらかに変わっているかを見ます。

よくある設計ミスと直し方

clamp()は書けるようになってからのほうが、判断を誤りやすい関数です。現場で見かける失敗を3つ挙げます。

1. 全部の値を可変にしてしまう

文字も余白もボーダーも角丸も可変にすると、画面幅を変えたときに全部が同時に動いて落ち着かない見た目になります。可変にするのは見出し・セクション余白・コンテナ幅の3つで十分です。ボーダー幅やアイコンサイズは固定のほうが安定して見えます。

2. 上限と下限の幅を広げすぎる

/* 悪い例:3倍以上動く */
h2 { font-size: clamp(1rem, 6vw, 3.5rem); }

/* 良い例:1.4〜1.6倍に収める */
h2 { font-size: clamp(1.4rem, 1.09rem + 1.33vw, 2rem); }

最小と最大の比は1.4〜1.8倍が扱いやすい範囲です。それ以上開くと、中間の画面幅で意図しないサイズになります。デザインカンプが2種類(スマホ・PC)しかないなら、その2点を結ぶ値に収めてください。

3. 見出しの階層が逆転する

h1とh2で係数が違うと、特定の画面幅でh2のほうが大きくなることがあります。変数で管理していれば起きにくいですが、個別に書いていると発生します。

/* 検証:320px〜1920pxで階層が保たれるか確認する
   h1: clamp(1.75rem, 1.18rem + 2.42vw, 3rem)
   h2: clamp(1.4rem,  1.09rem + 1.33vw, 2rem)
   → どの幅でも h1 > h2 になっている */

チェックは最小幅と最大幅の両端で行えば十分です。両端で階層が保たれていれば、直線なので中間でも逆転しません。

確認する幅見るポイント期待する状態
320px下限で止まっているか本文16px・見出しが潰れない
768px中間でなめらかか階層が保たれている
1200px上限に達したか設計どおりのサイズ
1920px上限で止まるか1200pxと同じサイズ

この4点だけ見れば、clamp()の設定ミスはほぼ検出できます。ブラウザの幅を手で動かしながら、文字が飛ばずに変化するかを確認してください。


まとめ

clamp(最小, 推奨, 最大)は、画面幅に応じてなめらかに変わる値を1行で書ける関数です。第2引数をrem + vwの形にすると、可変にしながらブラウザの文字拡大設定にも反応します。vw単独は拡大が効かず下限も無いため実用には向きません。可変にすべきは見出しと余白で、本文は16〜18pxの狭い範囲に留めます。レイアウトの段階的な切り替えは引き続きメディアクエリの仕事なので、連続量はclamp()、切替はメディアクエリ、と分けて使うのが整理された書き方です。

よくある質問(FAQ)

clamp()の第2引数はどう決めればいいですか?

狙う2点を決めて直線の式を求めます。375pxで28px、1200pxで48pxにしたいなら、傾きは(48−28)÷(1200−375)=0.0242、これを100倍して2.42vw。切片は28−(0.0242×375)=18.9px≒1.18remとなり、clamp(1.75rem, 1.18rem + 2.42vw, 3rem)になります。計算が面倒なら記事内の早見表の値をそのまま使ってください。

font-size: 5vw だけではだめですか?

2つの問題があります。1つは下限と上限が無いため、狭い画面で読めないほど小さくなり、大画面では巨大になること。もう1つはブラウザの文字サイズ拡大設定にまったく反応しないことです。アクセシビリティの観点から、vw単独の指定は避けてください。

本文の文字サイズにも使うべきですか?

使えますが、変化幅は16px〜18px程度に留めてください。本文が画面幅で大きく動くと読みにくくなります。可変の効果が大きいのは見出しとセクションの余白で、本文はほぼ固定でも問題ありません。

メディアクエリは不要になりますか?

なりません。文字サイズや余白のような連続的に変わる値はclamp()が適していますが、1カラムから2カラムへの切り替えや要素の表示・非表示のような段階的な変化はメディアクエリでしか表せません。役割を分けて併用するのが正解です。

幅の指定にclamp()を使ったら横スクロールが出ました。

vwはスクロールバーの幅を含むため、100vwを使うと本文領域からはみ出します。幅の指定には%を使い、width: min(92%, 760px)のように書いてください。

あわせて読みたい関連記事

CSS・Webデザイン実装の総まとめ

CSSの実装テクニックを「レイアウト・装飾・アニメーション・基礎・トラブル解決」の目的別に探せる総まとめページを用意しています。実装で迷ったときの索引としてどうぞ。

AIスキルで、未来の自分をアップデート!

今なら完全無料でAIを学べる!WithAI

今なら完全無料でAIを学べる!

  • 動画や実践で楽しく学べる:初心者でも安心のカリキュラム
  • スマホ・PCどちらでもOK:好きな時間に学習できる
  • 料金は一切ナシ0円でAIスキルが身につく

目的に合わせて選べる「AI副業」「AI転職」「AI活用」の3コースを用意。副収入・キャリアチェンジ・日常の生産性アップまで、あなたのゴールに合わせてAIを学べます。

会員登録はカンタン30秒で完了します。まずは公式LINEから、無料でAI学習をスタートしましょう!

この記事を書いた人

WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次