CSSのcalc()関数とは
calc()
を使うと、プロパティ値の中で数値の四則演算ができる。単純な数値同士の計算はSassなどのプリプロセッサでも可能だが、calc()
は異なる単位系同士の足し引きができるのが便利なポイントだ。
.foo {
// SCSSなら同じ単位同士の足し引きは可能だが…
margin-top: 30px + 30px;
// これはコンパイルエラー
margin-top: 2em + 30px;
// 異なる単位系同士の演算はcalc()が必要
margin-top: calc(2em + 30px);
}
calc()での加算減算は単位なしのゼロを受け付けない
このとき通常のプロパティ値と異なる点として、たとえ0であったとしても、単位のない数値の利用に制約があるという仕様がある。
That is, width: calc(0 + 5px); is invalid, even though both width: 0; and width: 5px; are valid.
CSS Values and Units Module Level 3: 8.1.2. Type Checking
もちろん、掛け算の係数や割り算の除数に単位なしの値を使うことは問題なくできる。しかし、calc()
の中の足し算・引き算に単位のない数値が含まれていると、calc()
を使った値を含むプロパティ全体が無効になる。これは数値が0のときも例外ではない。
.foo {
// これは当然問題がない
margin-top: 0 + 100px; // 1
}
.foo {
// これはNG
margin-top: calc(0 + 10px); // 2
}
上の例の場合// 2
の行全体が無視されるので .foo は margin-top: 100px; が適用される。
-
calc(0 + 10px)
のようなブラウザにとって無効な記述があっても、Sassのコンパイルエラーにはならない - Sassの通常の演算機能で
0 + 10px
や10 + 10px
は普通に計算されるので一見違和感がない -
margin-top: 0;
のような書き方はCSSで普通にする - sass-lintで
0
からは単位を外す書き方を推奨されることが多い(zero-unit
ルール) - あんまりネットでこの仕様が紹介されていない(ような気がする)
このような分かりにくさがあり、calc()
のこの挙動は気付きにくい。
とはいえ、あえてゼロをcalc()
の中に混ぜたいというのは中々実際の作業としては考えにくい。本当に注意が必要なのはcalc()
とCSS変数を組み合わせた場合だ。
calc()とCSS変数を組み合わせたときは特に注意
calc()
が単位なしの0を加算減算で認めないという仕様は、CSS変数を組み合わせたときも変わらない。直接CSS変数の値がどうなっているのかが見えないため、値が単位なしの数値のときが更に分かりにくい。
:root {
--my-margin: 30px;
--foo-margin: 2em;
}
.foo {
// これだけだったらもちろん有効
margin-top: calc(var(--my-margin) + var(--foo-margin));
}
.bar {
// もし .foo の外側でCSS変数が上書かれていたら……
--foo-margin: 0;
}
<p class="foo">このfooは margin-top: calc(30px + 2em); になる</p>
<div class="bar">
<p class="foo">このfooは margin-top が解釈されない!</p>
</div>
上の例のように、CSS変数は普通のプロパティと同じように上書きができる。自分の意図しないところでいつのまにか値が上書かれてしまっていたりすると、あるところでは正常に動いていたcalc()
がある場所では動かないということも十分起こりうる。
単位のないゼロによるバグに注意
このcalc()
の仕様を把握しておけば、意図しないスタイルの挙動が発生したときにデバッグがしやすくなる。CSS変数とcalc()
の組み合わせは便利な場合もあるが、意図せず単位のない値が入らないかを十分に注意したい。