clamp() による流体サイジング
問題
従来のレスポンシブデザインは、特定のブレークポイントでサイズを変更するメディアクエリに依存しています。これは急激な変化を生みます。見出しがモバイルでは 2rem で、デスクトップでは突然 3rem になるといった具合です。AIエージェントはフォントサイズ、パディング、幅に対して複数のメディアクエリを生成しがちで、冗長で保守しにくいCSSになります。clamp() 関数は、1つの宣言でメディアクエリゼロのまま、最小値と最大値の間をスムーズに流体スケーリングすることでこの問題を解決します。
解決方法
clamp() 関数は3つの値を取ります:
clamp(minimum, preferred, maximum)
- minimum — 値が取りうる最小値
- preferred — 流体的にスケールするビューポート相対の式
- maximum — 値が取りうる最大値
ブラウザは preferred 値を使用しますが、minimum と maximum の間に制約します。preferred 値が minimum より小さい場合は minimum が使われ、maximum より大きい場合は maximum が使われます。
コード例
流体タイポグラフィ
h1 {
font-size: clamp(1.75rem, 1rem + 2.5vw, 3rem);
}
h2 {
font-size: clamp(1.375rem, 0.875rem + 1.5vw, 2rem);
}
p {
font-size: clamp(1rem, 0.875rem + 0.4vw, 1.25rem);
}
見出しは狭いビューポートの 1.75rem から広いビューポートの 3rem までスムーズにスケールします。1rem + 2.5vw の preferred 値は固定のベースとビューポート相対の部分を組み合わせており、ユーザーがズームした場合でもテキストがスケールします。
rem + vw であって、vw だけではない理由
/* Bad: pure vw ignores user zoom preferences */
h1 {
font-size: clamp(1.75rem, 4vw, 3rem);
}
/* Good: rem + vw respects user zoom */
h1 {
font-size: clamp(1.75rem, 1rem + 2.5vw, 3rem);
}
preferred 値に vw だけを使うと、ユーザーがブラウザのズームレベルを変更してもテキストがスケールしません(WCAGアクセシビリティ要件に違反します)。rem + vw を組み合わせることで、テキストがビューポート幅とズーム設定の両方に応答します。
preferred 値の計算方法
最小ビューポートでの最小サイズから最大ビューポートでの最大サイズまで線形にスケールするには:
slope = (maxSize - minSize) / (maxViewport - minViewport)
intercept = minSize - slope × minViewport
preferred = intercept(rem) + slope × 100(vw)
例:320px で 1.75rem から 1280px で 3rem にスケール:
slope = (3 - 1.75) / (80 - 20) = 1.25 / 60 = 0.02083
intercept = 1.75 - 0.02083 × 20 = 1.3334
preferred = 1.3334rem + 2.083vw
(ビューポート幅は16で割って rem に変換:320/16=20、1280/16=80)
h1 {
font-size: clamp(1.75rem, 1.333rem + 2.083vw, 3rem);
}
実際には、手計算ではなく clamp 計算ツールを使いましょう。
流体スペーシング
clamp() はタイポグラフィだけでなく、padding、margin、gap にも使えます:
.section {
padding-block: clamp(2rem, 1rem + 3vw, 5rem);
}
.card-grid {
gap: clamp(1rem, 0.5rem + 1.5vw, 2.5rem);
}
.container {
padding-inline: clamp(1rem, 0.5rem + 2vw, 4rem);
}
流体幅の制約
.content {
max-inline-size: clamp(30rem, 90%, 60rem);
}
コンテンツエリアは最小 30rem、最大 60rem で、その間はコンテナの90%にスケールします。
完全な流体タイポグラフィシステム
:root {
--text-sm: clamp(0.875rem, 0.8rem + 0.2vw, 1rem);
--text-base: clamp(1rem, 0.875rem + 0.4vw, 1.25rem);
--text-lg: clamp(1.25rem, 1rem + 0.75vw, 1.75rem);
--text-xl: clamp(1.5rem, 1.125rem + 1.25vw, 2.25rem);
--text-2xl: clamp(1.875rem, 1.25rem + 2vw, 3rem);
--text-3xl: clamp(2.25rem, 1.25rem + 3vw, 4rem);
--space-sm: clamp(0.5rem, 0.375rem + 0.4vw, 0.75rem);
--space-md: clamp(1rem, 0.75rem + 0.75vw, 1.5rem);
--space-lg: clamp(1.5rem, 1rem + 1.5vw, 3rem);
--space-xl: clamp(2rem, 1rem + 3vw, 5rem);
}
メディアクエリの置き換え
/* Before: multiple breakpoints */
h1 {
font-size: 1.75rem;
}
@media (min-width: 640px) {
h1 { font-size: 2rem; }
}
@media (min-width: 768px) {
h1 { font-size: 2.5rem; }
}
@media (min-width: 1024px) {
h1 { font-size: 3rem; }
}
/* After: one line, smooth scaling */
h1 {
font-size: clamp(1.75rem, 1rem + 2.5vw, 3rem);
}
AIがよくやるミス
- preferred 値に
vwだけを使っている。font-size: clamp(1rem, 3vw, 2rem)はユーザーがブラウザをズームしてもスケールせず、WCAG 1.4.4に違反します。preferred 値では常にrem+vwを組み合わせましょう。 clamp()の方がシンプルなのに、フォントサイズに複数のメディアクエリを生成している。 1つのclamp()宣言で3〜4個のメディアクエリを置き換えられ、よりスムーズなスケーリングが実現できます。- min と max を逆にしている。 minimum 値が maximum より大きい場合、
clamp()は常に minimum を返します。min < maxであることを必ず確認しましょう。 - min と max の値に
remではなくpxを使っている。remを使うことで、ユーザーのフォントサイズ設定やズーム設定が尊重されます。 - スペーシングに
clamp()を使っていない。 AIエージェントはフォントサイズにはclamp()を使う一方で、padding、margin、gap にはまだメディアクエリを使うことがあります。これらすべてが流体スケーリングの恩恵を受けます。 - preferred 値を複雑にしすぎている。 式はピクセルパーフェクトである必要はありません。本文テキストには
clamp(1rem, 0.875rem + 0.5vw, 1.25rem)で十分です。特定のビューポート幅間の正確な線形補間が必要になることはほとんどありません。 clamp()を不必要にcalc()の中にネストしている。clamp()はすでに内部で数式を評価します。calc(clamp(...))は冗長です。
使い分け
clamp() が最適な場面
- モバイルとデスクトップ間でスムーズにスケールすべきフォントサイズ
- ビューポートに応じて大きくなるセクションの padding と margin
- 柔軟な制約を持つコンテナ幅
- grid/flex レイアウトの gap 値
- 現在2つ以上のメディアクエリを使ってスケールしている値すべて
メディアクエリを使い続ける場面
- プロパティをまったく別の値に変更する必要がある場合(スケールされた版ではなく)
- サイズのスケーリングではなくレイアウトの切り替え(例:1カラムからサイドバーへの切り替え)
- スケーリングが線形でない場合(例:特定のブレークポイントでサイズがジャンプすべき場合)
アクセシビリティ要件
- preferred 値では常に
rem+vwを使い、vwだけは避けましょう - ブラウザの200%ズームでテキストが読みやすいことをテストしましょう
- ビューポート全体で本文テキストが45〜75文字の行長範囲に収まることを確認しましょう