記事を検索

clamp()を使った流体タイポグラフィの設計

このページでは、CSSのclamp()関数を使った流体タイポグラフィの基本的な使い方と、デザイントークンとして管理する方法について紹介します。

流体タイポグラフィとは

従来のレスポンシブ設計では、画面幅ごとにメディアクエリを記述し、フォントサイズを段階的に切り替えるのが一般的でした。しかしこの方法では、「急にサイズが変わる」不自然さや、ブレークポイントが増えるほどコードが複雑化し、管理コストが増加する問題がありました。

流体タイポグラフィは、clamp()関数を使って最小値〜最大値の範囲でフォントサイズを滑らかに変化させる手法です。これにより、メディアクエリの大量記述を減らしつつ、どの画面サイズでも自然な見た目を維持できます。

clamp()の基本構文

clamp()は次のように3つの値を指定して使用します。

clamp(最小値, 推奨値, 最大値)

「推奨値」は、通常vwなどのビューポート単位を含めて記述します。画面が狭いときは最小値、広いときは最大値で頭打ちしながら、その間の幅では推奨値の式に従って連続的に変化します。

まずはシンプルな例を見てみましょう。 最小2rem、最大4remの範囲で、8vwの推奨値を基準に画面幅に応じてフォントサイズが滑らかに変化させてみます。

<p class="ex--clamp">Hellow World !</p>
.ex--clamp{
font-size: clamp(2rem, 8vw, 4rem);
}

実際に確認してみるとこんな感じです。

Demo Preview
別タブで表示 ↗

プレビューエリアをリサイズするか、別タブで確認してウインドウサイズを動かしてみてください。フォントサイズが変わっていくのが確認できます。

最小値/最大値に達する画面サイズ を意識する

さて、この時、最小値/最大値に達する画面サイズはどれくらいでしょうか。
clamp() を使う時は変化させたい “画面幅の範囲” を意識して実装することも重要です。

1remがデフォルト値の16pxとして、求めたいサイズをW として計算すると、

最小値 | 最大値
-----------------------+----------------------
W * 0.08 = 2rem = 32px | W * 0.8 = 4rem = 64px
W = 32 / 0.08 | W = 64 / 0.8
W = 400px | W = 800px

つまり、400pxで最小値2rem ~ 800pxで最大値4rem の間で可変することがわかります。

メディアクエリとの比較

同様の内容をメディアクエリで表現すると、以下のような記述が必要になります。

CSS
.ex--clamp {
font-size: 2rem;
}
@media (min-width: 400px) {
.ex--clamp {
font-size: calc(8vw);
}
}
@media (min-width: 800px) {
.ex--clamp {
font-size: 4rem;
}
}

clamp()を使えば1行で済む処理が、メディアクエリでは複数のブレークポイントを設定する必要があることがわかります。

ツールを用いて clamp の値を計算する

どうでしょうか、800pxで最大値に達するタイミングが少し早すぎるかもしれませんね。

では、1200pxで最大値に達するように調整したくなったらどうすればいいでしょう?

単純にvwだけを使うだけだとその条件は満たすことができません…。
んー…計算するの大変ですね…。

そういう時はツールに頼りましょう。「Min-Max-Value Interpolation」などの無料の計算ツールが数多く公開されているので、それらを活用するとよいでしょう。

Min-Max-Value Interpolation A tool that calculates the CSS clamp formula to interpolate between two values in a given viewport range.
min-max-calculator.9elements.com

ツールに「最小値」「最大値」「変化させたい画面幅」を設定するだけで、滑らかな補間計算をしたclamp()の式を生成してくれます。精度を求める場合や複数サイズを定義する場合に非常に便利です。

実際に使ってみます。

ツールをしている画面のスクリーンショット

答えがでてきました。

clamp(2rem, 1rem + 4vw, 4rem) とすることで、400px時の2remを下限、1200px時の4remを上限として自動調整が行われます。

流体サイズをベースにしたスケーリング規則の導入

サイト全体のベースとなるフォントサイズをclamp()を使って流体サイズで定義し、それをもとに他のフォントサイズを相対的にスケールさせた値をデザイントークンとして定義しておくと便利です。

(以下の例では、360px ~ 1400px で 1rem ~ 2rem と、大袈裟に変化させています)

Demo Preview
別タブで表示 ↗
:root {

	/* 基準となるフォントサイズをclamp() で定義 */
	--fz--base: clamp(1rem, 0.654rem + 1.54vw, 2rem); /* わかりやすく大袈裟に変化させています */

	/* --fz-base を元にスケーリングさせる */
	--fz--xs: calc(var(--fz--base) * (8 / 10));
	--fz--s: calc(var(--fz--base) * (8 / 9));
	--fz--l: calc(var(--fz--base) * (8 / 7));
	--fz--xl: calc(var(--fz--base) * (8 / 6));
	--fz--2xl: calc(var(--fz--base) * (8 / 5));
	--fz--3xl: calc(var(--fz--base) * (8 / 4));
	--fz--4xl: calc(var(--fz--base) * (8 / 3));
}

/* bodyにベースのフォントサイズをセット */
body{
	font-size: var(--fz--base);
}

/* ユーティリティクラス */
.fz--base {
	font-size: var(--fz--base);
}
.fz--xs {
	font-size: var(--fz--xs);
}
.fz--s {
	font-size: var(--fz--s);
}
.fz--l {
	font-size: var(--fz--l);
}
.fz--xl {
	font-size: var(--fz--xl);
}
.fz--2xl {
	font-size: var(--fz--2xl);
}
.fz--3xl {
	font-size: var(--fz--3xl);
}
.fz--4xl {
	font-size: var(--fz--4xl);
}

それぞれのトークン全てにclampを使って微調整したサイズを定義してもいいですが、この例ではベースサイズを元にしたハーモニックモジュラースケーリングを採用しています。

/layout/typography-scaling/

余白の流体スケーリング

先ほど定義したベースのフォントサイズ--fz--baseを余白のスケーリングの単位としても利用することで、流体タイポグラフィに合わせた余白を、どの画面サイズでも維持できるようになります。

:root {
/* 余白のスケーリング単位 */
--s-unit: calc(var(--fz--base) * 0.5);
/* スペーシング単位を1とするフィボナッチ数列でスケーリングさせる */
--s--10: var(--s-unit);
--s--20: calc(2 * var(--s-unit));
--s--30: calc(3 * var(--s-unit));
--s--40: calc(5 * var(--s-unit));
--s--50: calc(8 * var(--s-unit));
--s--60: calc(13 * var(--s-unit));
}

フィボナッチ数列でのスケーリングについても、以下のページで紹介しています。

余白とタイポグラフィのスケーリング設計

html(:root) にclamp()のフォントサイズを設定する場合の注意点

先ほどの例では、body に --fz--base をセットしています。

これをHTMLにセットすれば、body以下では 1remclamp で定義された値となるため、余白のスケーリングにも単純に rem を使えば良いことになります。

:root {
font-size: var(--fz--base);
/* 余白のスケーリング単位 */
--s-unit: calc(var(--fz--base) * 0.5);
--s-unit: 0.5rem;
}
body{
font-size: var(--fz--base);
font-size: 1rem;
}

こっちのほうが少しシンプルになりますね。

ただし注意点がいくつかあります。

  • clampでremではなくpxを使うと、ブラウザのフォントサイズ設定の変更が反映されない。
  • clampの推奨値(二番の引数)をvw単位のみで使用すると、ブラウザのフォントサイズ設定の変更が反映されない。
  • body内のどこかで 再びremを使ってclamp()を再定義しようとすると、すでにremがルートで流体になっているので、おかしなことになる。

なので、基本的にはベースフォントサイズはbodyに設定するのが良いのかなーと思います。

まとめ

  • clamp()で滑らかなフォントサイズ変化を実現: メディアクエリによる段階的な切り替えではなく、最小値〜最大値の範囲で連続的にサイズが変化するため、どの画面幅でも自然な見た目を保てます。
  • 計算ツールを活用して効率的に設計する: 変化させたい画面幅の範囲と最小・最大値を指定すれば、ツールが適切なclamp()の式を自動生成してくれます。
  • 基準サイズを流体化し、余白もスケーリングさせる: --fz--baseなどの基準サイズをclamp()で定義し、他のフォントサイズや余白をそこから相対的に算出することで、一貫性のあるレスポンシブデザインが構築できます。