- CSS
CSS calcの使い方|min・max・clampと効かない原因一覧
CSSのcalc()とは、width: calc(100% - 20px) のように、単位の違う値どうしを足し引き・掛け割りしてプロパティの値にできる関数です。 仲間の関数に、小さいほうを選ぶ min()、大きいほうを選ぶ max()、下限と上限のあいだに収める clamp() があり、この4つを組み合わせると、メディアクエリを書かなくても画面幅に合わせて伸び縮みするサイズを1行で指定できます。
この記事では、4つの関数の書き方・使える単位・典型用途を早見表にまとめ、コピペ用コードと「効かない」ときの原因を1つずつ再現しました。計算結果はすべて Playwright(Chromium 141.0.7390.37)で getComputedStyle() と CSS.supports() の値を読み取ったもの、Sass の結果は Dart Sass 1.105.1 でコンパイルしたものです。
calc・min・max・clampの早見表
| 関数 | 書き方 | 意味 | 典型的な用途 |
|---|---|---|---|
calc() |
calc(100% - 20px) |
四則演算(+ - * /)の結果 |
ヘッダー分を引いた高さ、余白を引いた幅、変数の掛け算 |
min() |
min(100%, 600px) |
引数のうちいちばん小さい値 | 「最大600pxまで」の幅(上限を付ける) |
max() |
max(1rem, 2.5vw) |
引数のうちいちばん大きい値 | 「最低1remは確保」の文字や余白(下限を付ける) |
clamp() |
clamp(1rem, 2.5vw, 2rem) |
clamp(最小, 推奨, 最大)。推奨値を最小〜最大に収める |
可変フォントサイズ、余白の上限下限 |
min() は「上限を付ける」、max() は「下限を付ける」と、名前と役割が逆に感じられる点が最初のつまずきどころです。min(100%, 600px) は「100% と 600px の小さいほう」なので、結果として600pxを超えません。
使える値と書き方のルール
| 項目 | ルール | 例 |
|---|---|---|
| 使える型 | 長さ・%・数値・角度・時間など。結果の型がプロパティに合っていること | calc(1rem + 2vw)、calc(var(--i) * 30deg) |
| 単位の混在 | 同じ種類(長さどうしなど)なら自由に混ぜられる | calc(50vw - 2rem)、min(100% - 32px, 720px) |
+ と - |
前後に空白が必要 | calc(100% - 20px) |
* と / |
空白はなくても動く。掛け算は片方が単位なしの数値 | calc(100% / 3)、calc(16px * 1.5) |
| 0 の扱い | 0 にも単位が必要(0px) |
calc(100% - 0px)、max(0px, 10px) |
| 入れ子 | かっこ、関数の中に関数、どちらも書ける | calc(100% - max(16px, 5%)) |
| 関数の中の式 | min・max・clamp の引数にはそのまま式を書ける(calc は不要) | min(100% - 32px, 720px) |
| 大文字小文字 | 区別しない | CALC(100% - 20px) も有効 |
効かない原因の早見表
| 症状・書き方 | 原因 | 直し方 |
|---|---|---|
calc(100% -20px)、calc(100%-20px) |
+ - の前後に空白がない |
calc(100% - 20px) |
calc(10px + 0)、max(0, 10px) |
単位なしの 0 | 0px と書く |
calc(10px * 10px) |
単位どうしの掛け算 | 片方を数値に:calc(10px * 10) |
calc(100px / 2px) を width に指定 |
結果が単位なしの数値になる | calc(100px / 2)、または calc(100px / 2px * 1px) |
--gap: 16; で calc(var(--gap) + 10px) |
カスタムプロパティの単位抜け | calc(var(--gap) * 1px + 10px)、または --gap: 16px |
Sass で calc(100%-#{$g}) |
空白なしのままCSSに出力される | 空白を入れる。calc(100% - $g) |
Sass で math.min(100%, 600px) がエラー |
Sass の関数が単位を比べられない | CSS の min(100%, 600px) をそのまま書く |
calc (100% - 20px) |
関数名とかっこのあいだに空白 | 詰めて書く |
clamp(200px, 50%) |
引数が2つ | 3つ書く。2つなら min() か max() |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS calcの使い方|min・max・clampと効かない原因一覧」
calc()の書き方
基本の形と実測値
幅800pxの親の中に要素を置き(画面は 1000×600px、ルートの文字サイズは16px)、width にさまざまな式を指定して計算値を調べました。
<!DOCTYPE html>
<style>
body { margin: 0; }
.p { width: 800px; height: 400px; font-size: 16px; }
#t { width: 111px; /* 目印。下の行が無効ならこの値が残る */
width: calc(100% - 20px); } /* ← ここを入れ替えて調べる */
</style>
<div class="p"><div id="t">x</div></div>
| 書き方 | CSS.supports | width の計算値 | メモ |
|---|---|---|---|
calc(100% - 20px) |
true | 780px | 基本形 |
calc(100% + 20px) |
true | 820px | |
calc(100% - -20px) |
true | 820px | 負の値を引くと足し算になる |
calc(100%*0.5) |
true | 400px | * は空白なしでも有効 |
calc(100%/4) |
true | 200px | / も空白なしで有効 |
calc(100% - (2 * 16px)) |
true | 768px | かっこで優先順位を明示 |
calc(100% - 20px / 2) |
true | 790px | / が先に計算される(100% − 10px) |
calc((100% - 20px) / 2) |
true | 390px | かっこを付けると (800 − 20) ÷ 2 |
calc(calc(100% - 20px) / 2) |
true | 390px | 内側の calc はかっこと同じ |
calc(50vw - 2rem) |
true | 468px | 500px − 32px |
calc(10 * 10px)、calc(10px * 10) |
true | 100px | 数値はどちら側でもよい |
calc(100px) |
true | 100px | 演算なしでも有効 |
CALC(100% - 20px) |
true | 780px | 大文字でも有効 |
計算の順番は算数と同じで、掛け算・割り算が先、足し算・引き算が後です。「全体から余白を引いて3等分」のように、引き算を先にしたいときはかっこで囲みます。
数値を受け取るプロパティでも使える
calc() は長さ専用ではありません。結果の型がプロパティの求める型と合っていれば使えます。
| 指定 | 計算値 | メモ |
|---|---|---|
line-height: calc(1 + 0.5) |
24px(文字サイズ16pxの1.5倍) | 数値の計算 |
z-index: calc(1 + 1) |
2 | 整数の計算 |
z-index: calc(3 / 2) |
2 | 整数のプロパティでは四捨五入される |
opacity: calc(1 / 2) |
0.5 | |
font-size: calc(1em + 4px) |
20px | |
rotate: calc(var(--i) * 30deg)(--i: 3) |
90deg | 角度の計算 |
min()・max()・clamp()の書き方
同じ環境(親の幅800px)で width に指定した結果です。
| 書き方 | 計算値 | どう決まったか |
|---|---|---|
min(100%, 600px) |
600px | 800px と 600px の小さいほう |
min(100%, 1200px) |
800px | 800px と 1200px の小さいほう |
max(50%, 300px) |
400px | 400px と 300px の大きいほう |
max(50%, 500px) |
500px | 400px と 500px の大きいほう |
min(100% - 32px, 600px) |
600px | 引数に式をそのまま書ける |
min(600px) |
600px | 引数1つでも有効 |
clamp(200px, 50%, 600px) |
400px | 推奨値400pxが範囲内 |
clamp(500px, 50%, 600px) |
500px | 推奨値が最小より小さい → 最小 |
clamp(200px, 50%, 300px) |
300px | 推奨値が最大より大きい → 最大 |
clamp(600px, 50%, 300px) |
600px | 最小 > 最大のときは最小が勝つ |
clamp(200px,50%,600px) |
400px | カンマの後ろの空白は任意 |
calc(min(100%, 600px) - 20px) |
580px | calc の中で使える |
min(max(300px, 50%), 600px) |
400px | clamp(300px, 50%, 600px) と同じ意味 |
calc(100% - max(16px, 5%)) |
760px | 800px − 40px |
clamp(最小, 推奨, 最大) は max(最小, min(推奨, 最大)) と同じ計算です。最小と最大が逆転していても宣言は有効で、その場合は最小が採用されます。値が思ったとおりに変わらないときは、引数の順番(小さいほうが先)を確かめます。
画面幅を変えたときの font-size
| 指定 | 320px | 768px | 1200px | 1600px |
|---|---|---|---|---|
clamp(1rem, 2.5vw, 2rem) |
16px | 19.2px | 30px | 32px |
max(1rem, 2.5vw)(下限だけ) |
16px | 19.2px | 30px | 40px |
min(2rem, 2.5vw)(上限だけ) |
8px | 19.2px | 30px | 32px |
max() は下限だけ、min() は上限だけ、clamp() は両方、という違いが数字に表れています。
コピペ用コード
完成形のHTML
次のHTMLは、この記事で紹介するレシピをまとめたものです。保存してブラウザで開くと、そのまま動きます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root { --header-h: 64px; }
body { margin: 0; }
/* 1. ヘッダー分を引いた高さ */
.header { height: var(--header-h); background: #eee; }
.main {
min-height: calc(100vh - var(--header-h)); /* 予備(dvh を知らないブラウザ向け) */
min-height: calc(100dvh - var(--header-h));
}
/* 2. 左右に16pxの余白を残しつつ、最大720px */
.box {
width: min(100% - 32px, 720px);
margin-inline: auto;
box-sizing: border-box;
/* 3. 余白の上限下限:16px〜48pxのあいだで画面幅の4% */
padding: clamp(16px, 4vw, 48px);
}
/* 4. 可変フォントサイズ:画面幅320pxで24px、1200pxで40px */
h1 { margin: 0; font-size: clamp(1.5rem, 1.1364rem + 1.8182vw, 2.5rem); }
/* 5. すき間24pxの3カラム(float や inline-block で並べるとき) */
.col { width: calc((100% - 2 * 24px) / 3); }
</style>
</head>
<body>
<header class="header"></header>
<main class="main">
<div class="box">
<h1>見出し</h1>
<div class="col">カラム</div>
</div>
</main>
</body>
</html>
画面サイズを変えて、各部分の大きさを測りました。
| 画面(幅×高さ) | .main の高さ | .box の幅 | 左の余白 | padding | h1 の文字サイズ |
|---|---|---|---|---|---|
| 320×568 | 504px | 288px | 16px | 16px | 24.0006px |
| 375×667 | 603px | 343px | 16px | 16px | 25.0007px |
| 768×1024 | 960px | 720px | 24px | 30.72px | 32.1462px |
| 1000×600 | 536px | 720px | 140px | 40px | 36.3644px |
| 1200×800 | 736px | 720px | 240px | 48px | 40px |
| 1440×900 | 836px | 720px | 360px | 48px | 40px |
| 1920×1080 | 1016px | 720px | 600px | 48px | 40px |
.main の高さは常に「画面の高さ − 64px」、.box は狭い画面で「幅 − 32px」、広い画面で720px、padding は16px〜48pxのあいだ、h1 は24px〜40pxのあいだに収まっています。
レシピごとの解説
1. ヘッダー分を引いた高さ
.main { min-height: calc(100dvh - var(--header-h)); }
height ではなく min-height にしておくと、中身が多いときに自然に伸びます。100dvh は、スマートフォンのアドレスバーの出入りに合わせて変わる画面の高さです。PC相当の環境では 100vh・100dvh・100svh のどれも同じ値(画面600pxで536px)になりました。スマートフォン実機での違いは今回は未実行です。
2. 余白つきの最大幅
.box { width: min(100% - 32px, 720px); margin-inline: auto; }
max-width: 720px と左右の padding を組み合わせる書き方を、1行で表せます。
3. 余白の上限下限
.section { padding-block: clamp(32px, 8vw, 96px); }
4. 可変フォントサイズ
h1 { font-size: clamp(1.5rem, 1.1364rem + 1.8182vw, 2.5rem); }
真ん中の推奨値の作り方は、次の章で説明します。
5. すき間を引いて等分する
.col { width: calc((100% - 2 * 24px) / 3); } /* 親800pxなら 250.656px */
flex や grid の gap を使う場合は、この計算は不要です。float や inline-block で並べるとき、あるいは flex-basis に具体的な幅を渡したいときに使います。
6. 親の幅を超えて画面いっぱいに広げる
.full-bleed { margin-inline: calc(-1 * (100vw - 100%) / 2); }
中央寄せされた親の中から、画像だけを画面の両端まで広げる書き方です。画面1000px・親800pxのとき、margin-left は -100px になりました。縦スクロールバーが幅を取る環境では、100vw がスクロールバーぶん広くなる点に注意します。
clampの推奨値の計算式
可変フォントサイズの真ん中の値(1.1364rem + 1.8182vw)は、「この画面幅でこのサイズ」という2点を通る直線の式です。
| 求めるもの | 計算式 | 例:320pxで24px、1200pxで40px |
|---|---|---|
| 傾き | (最大サイズ − 最小サイズ)÷(最大画面幅 − 最小画面幅) | (40 − 24) ÷ (1200 − 320) = 0.018182 |
| vw の係数 | 傾き × 100 | 1.8182vw |
| 切片(px) | 最小サイズ − 傾き × 最小画面幅 | 24 − 0.018182 × 320 = 18.18px |
| 切片(rem) | 切片 ÷ 16 | 1.1364rem |
| 完成形 | clamp(最小rem, 切片rem + 係数vw, 最大rem) | clamp(1.5rem, 1.1364rem + 1.8182vw, 2.5rem) |
毎回手で計算しなくて済むよう、Node.js で動くスクリプトにしました。
// fluid.js(Node.js 22 で実行)
// 使い方: node fluid.js 最小px 最大px [最小画面幅px 最大画面幅px]
function fluid(minPx, maxPx, minVw = 320, maxVw = 1200, rootPx = 16) {
const slope = (maxPx - minPx) / (maxVw - minVw); // 傾き
const intercept = minPx - slope * minVw; // 切片
const r = n => +n.toFixed(4);
return `clamp(${r(minPx / rootPx)}rem, ${r(intercept / rootPx)}rem + ${r(slope * 100)}vw, ${r(maxPx / rootPx)}rem)`;
}
const [a, b, c, d] = process.argv.slice(2).map(Number);
if (a) console.log(fluid(a, b, c || 320, d || 1200));
else for (const [mn, mx] of [[14,16],[16,18],[16,20],[20,28],[24,40],[32,56],[16,48]])
console.log(`${mn}px → ${mx}px :`, fluid(mn, mx));
$ node fluid.js
14px → 16px : clamp(0.875rem, 0.8295rem + 0.2273vw, 1rem)
16px → 18px : clamp(1rem, 0.9545rem + 0.2273vw, 1.125rem)
16px → 20px : clamp(1rem, 0.9091rem + 0.4545vw, 1.25rem)
20px → 28px : clamp(1.25rem, 1.0682rem + 0.9091vw, 1.75rem)
24px → 40px : clamp(1.5rem, 1.1364rem + 1.8182vw, 2.5rem)
32px → 56px : clamp(2rem, 1.4545rem + 2.7273vw, 3.5rem)
16px → 48px : clamp(1rem, 0.2727rem + 3.6364vw, 3rem)
$ node fluid.js 24 40 375 1440
clamp(1.5rem, 1.1479rem + 1.5023vw, 2.5rem)
「16px → 20px」の値をブラウザで確かめると、320pxで16px、768pxで18.0362px、1200pxで19.9996px、1600pxで20pxになり、指定した2点を通っていることがわかります。
推奨値に rem を混ぜる理由
推奨値を 4vw のように vw だけで書くと、ブラウザの文字サイズ設定を大きくしても、文字が大きくなりません。rem を混ぜておくと、設定に追従します。html の font-size を 100% と 200% に切り替えて測りました(画面幅1000px)。
| 指定 | html が 100% のとき | html が 200% のとき |
|---|---|---|
font-size: 4vw |
40px | 40px(変わらない) |
font-size: clamp(1rem, 0.5rem + 2vw, 2rem) |
28px | 36px(大きくなる) |
上の計算式で作った値は、切片が rem、最小と最大も rem なので、文字サイズ設定に追従します。
効かない原因を1つずつ再現して直す
calc() の書き方に誤りがあると、ブラウザはその宣言を1行まるごと読み飛ばします。エラーは表示されず、「何も指定していないのと同じ」状態になるため、原因に気づきにくいのが特徴です。ここでは、目印として直前に width: 111px を置き、111px が残っていれば「読み飛ばされた」とわかるようにして調べました。
1. + と - の前後に空白がない
| 書き方 | CSS.supports | 計算値 |
|---|---|---|
calc(100% -20px) |
false | 111px(無効) |
calc(100%-20px) |
false | 111px(無効) |
calc(100% +20px) |
false | 111px(無効) |
min(100%-32px, 600px) |
false | 111px(無効) |
z-index: calc(1+1) |
false | auto(無効) |
calc(100% - 20px) |
true | 780px |
-20px は「マイナス20px」という1つの値として読まれるため、100% -20px は「100% と −20px が並んでいるだけ」で演算子がない、と解釈されます。+ も同じです。一方、* と / は値の一部として読まれることがないので、空白なしでも動きます。4つの演算子すべてに空白を入れる習慣にしておくと迷いません。
コードの整形や圧縮のツールが空白を取り除いた結果、この形になることもあります。ビルド後のCSSで効かなくなったときは、出力されたファイルの calc() を確かめます。
2. 単位なしの 0
| 書き方 | CSS.supports | 計算値 |
|---|---|---|
calc(10px + 0) |
false | 111px(無効) |
calc(0 - 10px) |
false | 111px(無効) |
calc(100% - 0) |
false | 111px(無効) |
max(0, 10px)、min(10px, 0) |
false | 111px(無効) |
calc(10px + 0px) |
true | 10px |
max(0px, 10px) |
true | 10px |
ふだんは margin: 0 のように 0 の単位を省けますが、calc()・min()・max()・clamp() の中では、0 は「単位なしの数値」として扱われ、長さとは足し引きできません。0px と書きます。変数の初期値を --offset: 0 としておき、あとで calc(100% - var(--offset)) に使う、という流れで起きやすいので、長さとして使う変数は 0px で定義しておくと安心です。
3. 単位同士の掛け算・割り算
| 書き方 | CSS.supports | 計算値 | 理由 |
|---|---|---|---|
calc(10px * 10px) |
false | 111px(無効) | px × px は長さにならない |
calc(10px * 10) |
true | 100px | 片方が数値 |
calc(2 / 100px) |
false | 111px(無効) | 数値 ÷ 長さは長さにならない |
calc(100px / 2) |
true | 50px | 長さ ÷ 数値 |
calc(100px / 2px)(width に指定) |
false | 111px(無効) | 結果が数値(50)になり、width の型に合わない |
calc(100px / 2px * 1px) |
true | 50px | 数値に戻したあと 1px を掛けて長さにする |
calc(100px + 10) |
false | 111px(無効) | 長さと数値は足せない |
calc(100px / 0) |
true | 3.35544e+07px | 0 で割ると無限大扱いになり、ブラウザの上限値になる |
掛け算は「片方が数値」、割り算は「右側が数値」が基本です。長さ ÷ 長さで数値を取り出す書き方(calc(100px / 2px))は、Chromium 141 では結果を数値として使う場面(上の表の最後から3行目)で動きました。ほかのブラウザでの動作は今回は未実行なので、使う前に対象ブラウザで確かめてください。
4. Sass の min()・max() との衝突
Sass には、もともと同じ名前の min()・max() 関数があり、コンパイル時に値を比べようとします。Dart Sass 1.105.1 で試した結果です。
// input.scss
@use "sass:math";
$w: 600px;
$g: 20px;
$n: 16;
.a { width: min(100%, $w); } // ① 単位が違う
.b { width: min(10px, 20px); } // ② 単位が同じ
.c { font-size: clamp(1rem, 0.5rem + 2vw, 2rem); }
.d { width: calc(100% - $g); } // ③ calc の中に変数をそのまま
.e { width: calc(100% - #{$g}); } // ④ 従来の #{} でも同じ
.f { width: calc($w / 3); } // ⑤ 計算できるものは計算される
.g { width: calc(100% - $n * 1px); } // ⑥ 単位なしの変数に単位を付ける
$ npx sass --style=compressed input.scss
.a{width:min(100%,600px)}.b{width:10px}.c{font-size:clamp(1rem,.5rem + 2vw,2rem)}.d{width:calc(100% - 20px)}.e{width:calc(100% - 20px)}.f{width:200px}.g{width:calc(100% - 16px)}
現在の Dart Sass は、単位が違って比べられないときはCSSの関数としてそのまま出力し、比べられるときは計算して出力します。 つまずきやすいのは次の6つです。
| 書き方 | 結果 | 直し方 |
|---|---|---|
math.min(100%, 600px) |
Error: 100% and 600px have incompatible units. |
sass:math の関数ではなく、素の min(100%, 600px) を書く |
calc(100%-#{$g}) |
エラーにならず calc(100% -20px) が出力される(ブラウザでは無効) |
calc(100% - #{$g}) と空白を入れる |
calc(100% -$g) |
Error: "+" and "-" must be surrounded by whitespace in calculations. |
calc(100% - $g) |
calc(100% - $n)($n: 16) |
Error: 100% and 16 are incompatible. |
calc(100% - $n * 1px) |
min($n, 100%)($n: 16) |
エラーにならず width: 16 が出力される(ブラウザでは無効) |
変数に単位を付ける:$n: 16px |
:root { --x: $w; } |
--x: $w がそのまま出力される |
--x: #{$w}; と書く |
2行目と5行目は、コンパイルは通るのに、ブラウザで読み飛ばされるCSSが出力される組み合わせです。Sass 側でエラーが出ないので、出力されたCSSを見て確かめます。
なお、以前の Sass の処理系(LibSass や初期の Dart Sass)では、min(100%, 600px) と書くだけで単位が違うというエラーになり、unquote("min(100%, 600px)") のように文字列として渡す回避策が使われていました。この以前の処理系での動作は今回は未実行です。Dart Sass 1.105.1 では、この回避策がなくても ① のとおり出力されます。
5. カスタムプロパティの単位抜け
| 定義 | 使い方 | 計算値 | メモ |
|---|---|---|---|
--gap: 16; |
width: var(--gap) |
800px(auto) | 無効。目印の111pxではなく初期値に戻る |
--gap: 16; |
width: calc(var(--gap) + 10px) |
800px(auto) | 数値 + 長さは無効 |
--gap: 16; |
width: calc(var(--gap) * 1px) |
16px | 1px を掛けて長さにする |
--gap: 16; |
width: calc(var(--gap) * 1px + 10px) |
26px | |
--gap: 16px; |
width: calc(var(--gap) + 10px) |
26px | 最初から単位を付けておく |
--gap: 16px; |
width: calc(var(--gap) * 2) |
32px | |
--n: 5; |
width: var(--n)px |
800px(auto) | 変数の後ろに単位は付けられない |
--size: 120; |
width: calc(var(--size)px) |
800px(auto) | calc の中でも同じ |
--n: 0; |
width: calc(var(--n) + 10px) |
800px(auto) | 原因2と同じ。0px にする |
| (未定義) | width: calc(100% - var(--h)) |
800px(auto) | 変数が未定義 |
| (未定義) | width: calc(100% - var(--h, 0px)) |
800px | 予備の値を書いておく |
ここで注目したいのは、変数(var())を含む宣言が無効になったときは、直前の width: 111px に戻らず、プロパティの初期値(width なら auto)になる点です。ブラウザはCSSを読み込む段階では変数の中身がわからないため、いったん宣言を受け入れ、値を計算する段階で無効と判断します。そのため CSS.supports() は true を返し、DevTools でも警告マークが付かないことがあります。「幅が親いっぱいに広がった」「余白が0になった」ときは、変数の単位を疑います。
単位なしで定義した変数は、calc(var(--n) * 1px) のように「1(単位)」を掛けると、好きな単位の値に変えられます。
:root { --cols: 3; --i: 3; }
.grid { width: calc(var(--cols) * 100px + (var(--cols) - 1) * 16px); } /* 332px */
.hand { rotate: calc(var(--i) * 30deg); } /* 90deg */
符号を反転させたいときも calc を使います。-var(--gap) は無効で、calc(-1 * var(--gap)) か calc(var(--gap) * -1) と書きます(--gap: 16px のとき、どちらも -16px になりました)。
そのほかの書き方の誤り
| 書き方 | CSS.supports | 原因 |
|---|---|---|
calc (100% - 20px) |
false | 関数名と ( のあいだに空白がある |
calc(100% - 20 px) |
false | 数値と単位のあいだに空白がある |
calc(100% – 20px) |
false | ハイフンではなくダッシュ(–)が入っている。文書作成ソフトからの貼り付けで起きる |
calc(100)(width に指定) |
false | 結果が数値で、長さではない |
clamp(200px, 50%) |
false | 引数が2つ |
clamp(200px, 50, 600px) |
false | 真ん中の値に単位がない |
round(103.7px) |
false | 単位つきの値は、丸める間隔(第2引数)が必要 |
ブラウザで有効かどうかを確かめる方法
DevTools の Console に次の1行を入れると、その値が有効かどうかがすぐにわかります。
CSS.supports('width', 'calc(100% -20px)'); // false
CSS.supports('width', 'calc(100% - 20px)'); // true
CSS.supports('width', 'max(0, 10px)'); // false
DevTools の Styles タブでは、無効な宣言に打ち消し線と警告マークが付きます(この画面操作は今回は未実行で、上の CSS.supports() の結果は Playwright から同じ関数を呼んで確かめたものです)。var() を含む式は true が返っても計算時に無効になることがあるので、Computed タブで実際の値も確かめます。
round()・mod() など新しい数学関数
calc() の仲間は年々増えています。Chromium 141 で width に指定し、有効かどうかと計算値を調べました。
| 関数 | 例 | Chromium 141 の計算値 | 働き |
|---|---|---|---|
round() |
round(15px, 4px) |
16px | 4px刻みでいちばん近い値に丸める |
round(up, 15px, 4px) |
16px | 切り上げ | |
round(down, 15px, 4px) |
12px | 切り捨て | |
round(nearest, 100% / 3, 1px) |
267px | 800px ÷ 3 を整数のpxにそろえる | |
round(down, 100% / 3, 8px) |
264px | 8の倍数にそろえる | |
mod() |
mod(18px, 5px) |
3px | 割った余り(符号は割る数に合わせる) |
mod(-18px, 5px) |
2px | ||
rem() |
rem(18px, 5px) |
3px | 割った余り(符号は割られる数に合わせる) |
abs() |
abs(-20px) |
20px | 絶対値 |
sin()・cos()・tan() |
calc(sin(30deg) * 100px) |
50px | 三角関数 |
pow()・sqrt() |
calc(pow(2, 3) * 10px) |
80px | べき乗・平方根 |
hypot() |
hypot(30px, 40px) |
50px | 斜辺の長さ |
log()・exp() |
calc(log(100, 10) * 10px) |
20px | 対数・指数 |
定数 pi・e |
calc(pi * 10px) |
31.4062px | 円周率・自然対数の底 |
progress() |
calc(progress(50px, 0px, 100px) * 100px) |
50px | 範囲の中での割合(0〜1) |
sibling-index() |
calc(sibling-index() * 10px) |
10px(1番目の子) | 兄弟の中で何番目か |
calc-size() |
calc-size(auto, size + 10px) |
810px | auto などのサイズをもとに計算 |
random() |
random(10px, 100px) |
(無効) | Chromium 141 では CSS.supports が false |
round() と mod() は、MDNで「Baseline 2024(2024年5月から主要ブラウザの最新版で利用可能)」と案内されています。 そのほかの関数について、Chromium 以外のブラウザでの対応は今回は未実行・未確認です。新しい関数を使うときは、@supports で対応ブラウザにだけ適用する書き方が便利です(Chromium 141 で 200px が適用されることを確認)。
.card { width: 100px; } /* 先に、どのブラウザでも通る値 */
@supports (width: round(1px, 1px)) {
.card { width: round(down, 100% / 3, 8px); } /* 対応ブラウザだけ */
}
round() の使いどころは、割り切れない幅を整数のpxにそろえる場面です。calc(100% / 3) は 266.656px のような端数になりますが、round(nearest, 100% / 3, 1px) なら 267px にそろい、境界線のにじみを抑えられます。mod() を使うと、calc(100% - mod(100%, 120px)) で「120pxの倍数のうち、親に収まる最大の幅」(親800pxなら720px)を求められます。
Sass でこれらを使うときは、round() に注意します。Dart Sass 1.105.1 では、round(15px, 4px) は 16px、mod(18px, 5px) は 3px に計算されて出力され、round(up, 100% / 3, 1px) はCSSの関数としてそのまま出力されました。引数1つの round(10.5px) は Sass の関数として 11px に計算されますが、将来はCSSの round() として解釈されるという予告の警告が出ます。Sass の丸めを使いたいときは math.round() と書きます。
どの関数を選ぶかの判断表
| やりたいこと | 選ぶ関数 | 書き方の例 |
|---|---|---|
| 全体から決まった長さを引く | calc() | calc(100dvh - 64px) |
| すき間を引いて等分する | calc() | calc((100% - 48px) / 3) |
| 変数を掛けて値を作る | calc() | calc(var(--i) * 30deg) |
| 上限だけ付ける | min() | width: min(100%, 720px) |
| 下限だけ付ける | max() | padding: max(16px, 4vw) |
| 上限と下限を付ける | clamp() | font-size: clamp(1rem, 0.9rem + 0.5vw, 1.25rem) |
| 刻みにそろえる | round() | round(nearest, 100% / 3, 1px) |
| 並べるだけ(すき間つき) | 関数は不要 | flex・grid の gap |
書いたあとのチェックリスト
| 確認すること | 見るところ |
|---|---|
+ と - の前後に空白があるか |
ビルド後のCSSも確認 |
| 0 に単位が付いているか | 変数の初期値も含めて |
| 掛け算の片方、割り算の右側が数値になっているか | 単位どうしになっていないか |
| 変数の単位はそろっているか | 数値の変数には * 1px |
| clamp の引数は「最小・推奨・最大」の順か | 逆転すると最小が採用される |
| 文字サイズの推奨値に rem が入っているか | vw だけにしない |
| Sass の出力が意図したCSSになっているか | コンパイル後のファイルを見る |
よくある質問
Q. calc() の中で min() や clamp() を使えますか?
A. 使えます。calc(min(100%, 600px) - 20px) は 580px になりました。逆に、min()・max()・clamp() の引数には式をそのまま書けるので、min(calc(100% - 32px), 720px) と書かなくても min(100% - 32px, 720px) で同じ結果になります。
Q. calc(100% - 20px) の % は何に対する割合ですか?
A. そのプロパティで % を単独で書いたときと同じです。width なら親(包含ブロック)の幅、font-size なら親の文字サイズが基準になります。font-size: calc(100% + 2px) は、親が16pxのとき 18px になりました。
Q. メディアクエリと clamp() はどちらを使えばよいですか? A. 大きさをなめらかに変えたいところは clamp()、レイアウトそのものを切り替えたいところ(1カラムから2カラムへ、など)はメディアクエリが向いています。文字サイズや余白は clamp() に任せ、並び方の切り替えはメディアクエリで行う、という分け方がわかりやすいです。
Q. calc() の計算結果を確かめるにはどうすればよいですか?
A. DevTools の Computed タブで、計算後のpx値を見られます。Console で getComputedStyle($0).width と入力する方法もあります($0 は Elements パネルで選択中の要素)。この記事の計算値は、同じ getComputedStyle() で取得しています。
まとめ
- calc() は単位の違う値を計算できる関数。
+と-の前後には必ず空白を入れる。 - min() は上限、max() は下限、clamp() は両方を付ける。clamp(最小, 推奨, 最大) の順で書く。
- 関数の中では 0 にも単位が必要。 長さとして使う変数は
0pxで定義する。 - 掛け算は片方が数値、割り算は右側が数値。 単位なしの変数には
* 1pxを掛ける。 - var() を含む宣言が無効になると、直前の値ではなく初期値に戻る。 CSS.supports() が true でも計算値を確かめる。
- Sass では素の min()・max()・clamp() をそのまま書く。 コンパイルが通っても、出力されたCSSの空白と単位を確認する。
- round()・mod() は Baseline 2024。 そのほかの新しい関数は
@supportsと組み合わせて使う。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「CSS calcの使い方|min・max・clampと効かない原因一覧」
株式会社RJCでは、CSSのcalcやclampを使ったサイズ指定の考え方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。