CSSのclamp()で、文字の大きさを画面の幅に合わせる方法

​​CSS​HTML,Playwright,アクセシビリティ,レスポンシブ

CSS の clamp() を font-size に使うと、メディアクエリーを書かずに、文字の大きさを画面の幅に合わせてなめらかに変えられます。狭い画面では最小値より小さくならず、広い画面では最大値より大きくなりません。

css
p {
  font-size: clamp(1rem, 0.75rem + 1.25vw, 1.5rem);
}

この1行で、画面の幅が 320px のとき 16px、960px のとき 24px になり、その間は、幅が広がった分だけ一定の割合で大きくなります。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。検証用のページを Playwright で開き、画面の幅を変えながら getComputedStyle で font-size の値を読みました。ブラウザの標準の文字サイズの変更は、Chrome DevTools Protocol の Page.setFontSizes で再現しました。Firefox と Safari、ブラウザの拡大縮小(ズーム)での表示は試していません。各ブラウザの対応バージョンは、互換性データ(@mdn/browser-compat-data 8.1.8)の値です。

clamp() は3つの値のうち、範囲に収まる値を選ぶ

clamp(最小値, 推奨値, 最大値) は、真ん中の推奨値を、最小値と最大値の間に収めた値になります。CSS Values and Units Level 4 の仕様では、clamp(MIN, VAL, MAX) は max(MIN, min(VAL, MAX)) とまったく同じ値を表す、とされています。

真ん中の値に、画面の幅の 1% を表す vw を入れると、画面の幅で文字の大きさが変わります。仕様にあるとおり、各引数の中では calc() を書かずに + や * を使えます。

検証環境で、冒頭の clamp(1rem, 0.75rem + 1.25vw, 1.5rem) を指定した段落の font-size を、画面の幅を変えて測りました。

画面の幅 font-size
280px 16px
320px 16px
400px 17px
480px 18px
640px 20px
800px 22px
960px 24px
1280px 24px
1600px 24px
横軸が画面の幅(200〜1600px)、縦軸が font-size のグラフ。320px までは 16px で横ばい、320px から 960px までは右上がりの直線で 24px まで上がり、960px より広いと 24px で横ばいになる
clamp(1rem, 0.75rem + 1.25vw, 1.5rem) の font-size を、画面の幅ごとに測った値

320px より狭いと最小値の 16px、960px より広いと最大値の 24px で止まります。その間は、画面の幅が 160px 広がるごとに 2px ずつ大きくなりました。

同じページを幅 360px、640px、960px で開いて、左上を切り取ったのが次の画像です。見出しにも、あとで説明する求め方で作った clamp() を指定しています。

画面の幅 360px、640px、960px で同じページを開いた画面の左上を、縦に3つ並べた画像。上から順に、見出し「猫の手帳」と本文「文字の大きさが変わります。」が大きくなっていく。見出しは 32.0px、37.3px、43.4px、本文は 16.5px、20.0px、24.0px
画面の幅だけを変えて、同じページを開いた様子

真ん中の式は「何pxの幅で何pxにしたいか」から求める

真ん中の式は、「画面の幅が A px のとき文字を X px、B px のとき Y px にしたい」という2つの点を通る直線です。次のように計算します。

  1. 傾き = (Y − X) ÷ (B − A)
  2. vw の前に付ける数 = 傾き × 100
  3. 切片 = X − A × 傾き(px)
  4. rem の前に付ける数 = 切片 ÷ 16

冒頭の例は、A = 320、X = 16、B = 960、Y = 24 です。傾きは 8 ÷ 640 = 0.0125 なので 1.25vw、切片は 16 − 320 × 0.0125 = 12px なので 0.75rem になります。最小値と最大値には X と Y を rem で書いて、clamp(1rem, 0.75rem + 1.25vw, 1.5rem) ができあがります。

割り切れない場合も同じです。見出しを「360px で 2rem(32px)、1200px で 3rem(48px)」にしたいとき、傾きは 16 ÷ 840 = 0.019047…、切片は 25.142…px です。小数第5位を四捨五入すると、次の式になります。

css
h1 {
  font-size: clamp(2rem, 1.5714rem + 1.9048vw, 3rem);
}

検証環境で測ると、幅 360px で 32px、1200px で 48px になり、その間の 570px、780px、990px では 35.9998px、39.9998px、43.9999px でした。計算どおりなら 36px、40px、44px です。0.0002px ほどの差は、1.5714rem と 1.9048vw の小数を丸めたことによるものです。

なお、手順4の「÷ 16」は、ブラウザの標準の文字サイズが 16px の場合の換算です。次の節のとおり、標準の文字サイズが変わると、rem で書いた部分の px の値も変わります。

真ん中の式に rem を混ぜると、ブラウザの文字サイズの設定が効く

vw だけで文字の大きさを決めると、ブラウザの標準の文字サイズを変えても、文字は大きくなりません。rem はルート要素の文字の大きさを基準にする単位で、ブラウザの標準の文字サイズを変えると、それに合わせて変わります。

検証環境で、次の3つの書き方を、ブラウザの標準の文字サイズ 16px と 24px で比べました。

標準の文字サイズ・画面の幅 2.5vw clamp(16px, 12px + 1.25vw, 24px) clamp(1rem, 0.75rem + 1.25vw, 1.5rem)
16px・375px 9.375px 16.6875px 16.6875px
16px・800px 20px 22px 22px
16px・1280px 32px 24px 24px
24px・375px 9.375px 16.6875px 24px
24px・800px 20px 22px 28px
24px・1280px 32px 24px 34px

vw だけの 2.5vw は、幅 375px で 9.375px まで小さくなり、標準の文字サイズを 24px にしても変わりません。px で書いた clamp() も、標準の文字サイズを変えると、数字はまったく同じままでした。

すべて rem で書いた clamp() だけが、標準の文字サイズに合わせて大きくなりました。標準を 24px にすると、最小値と最大値は 24px と 36px になり、切片の 0.75rem も 18px になります。そのため、幅 1280px では 18 + 16 = 34px になりました。

WCAG の達成基準 1.4.4(テキストのサイズ変更)の解説では、ウェブのコンテンツは、ブラウザが文字を大きくする働きを妨げてはいけないとされています。文字の大きさを画面の幅に合わせるときは、最小値・最大値・真ん中の切片を rem で書いておくと、上の表のとおり、ブラウザの文字サイズの設定に合わせて文字が大きくなります。

最小値が最大値より大きいと、最小値が勝つ

最小値と最大値を逆に書いてしまうと、clamp() は最小値を返します。仕様にも、最小値と最大値がぶつかる場合は最小値を優先する、と書かれています。

検証環境で、幅 800px のときに clamp(24px, 1vw, 16px) を指定すると、font-size は 24px でした。真ん中の 1vw(8px)でも、最大値の 16px でもありません。文字の大きさが変わらないときは、引数の順番が「最小、推奨、最大」になっているかを見直してください。

主要なブラウザで使える

MDN の clamp() のページの互換性の表と同じデータ(@mdn/browser-compat-data)では、clamp() は Chrome 79、Edge 79、Firefox 75、Safari 13.1、iOS の Safari 13.4、Android の Chrome 79 から使えます。

​Posted by 86