目次

はじめに

最近node-sassを使用しているプロジェクトで、CSS関数のmin()を使った時に、Internal Error: Incompatible units: 'px' and 'vw'.というコンパイルエラーが出ました。

min()pxvwなどの異なる単位であっても、比較してレンダリング結果が小さい方の値を適用できるとても便利なCSS関数なのですが、node-sass(記事作成時点で最新のv4.14.1)で次のSassコードをコンパイルしようとするとエラーが出てしまいます。

.hoge {
  width: min(100vw, 10px);
}
/*
Internal Error: Incompatible units: 'px' and 'vw'.
*/

そして、このエラーはmax()でも発生します。

Incompatible unitsって何

Incompatible unitsは、互換性のない単位を計算している時に発生するエラーです。例えばSassで次のような書き方をすると、Sassコンパイル時にはvwが何pxなのか判断できないため、計算できずにコンパイルエラーになります。

.hoge {
  width: #{100vw - 10px};
}
/*
Error: Incompatible units: 'px' and 'vw'.
        on line 2 of stdin
>>   width: #{100vw - 10px};

   -----------^
*/

これはSassの仕様上正しく、Dart Sassでも発生します。

ただし、CSS関数であるmin()max()はCSS側で計算するもので、Sassのコンパイルができないと困るので解決策を探しました。

解決策

これらを解決する方法はいくつかあります。

Sassのunquote()を併用する

このバグはSassの開発側も認識しており、Sassの公式ドキュメントのちょっとわかりにくい箇所(右向きの三角アイコンをクリックすると出てくる)に、LibSassだとまだ未サポートなのでSassのunquote()を一緒に使ってねと記載されていました。

unquote()は、コンパイル時に引数の文字列をそのまま出力するための関数なので、意図しないタイミングでmin()が実行されることを防ぎます。

.hoge {
  width: unquote("min(100vw, 10px)");
}

個人的にはちょっと冗長な感じがしてあまり好きではありません。

Sassのinterpolationを使う

unquote()を使うのとしくみとしては同じで、Sassのinterpolation(#{})を使ってSassに文字列と認識させることでコンパイルエラーを回避することも可能です。

.hoge {
  width: #{"min(100vw, 10px)"};
}

unquote()と同じ仕組みで短く記述できるので個人的にはこっちの方が好みです。ただし、コードを書いた人以外なぜこのように書いたのかわからないことの方が多いと思うので、コメントを記載しておいた方がよさそうです。

Dart Sassを使う

このバグはDart Sass(記事作成時点で最新のv1.26.10)ではすでに修正されているので、sassを使うことでこのエラーは出なくなります。

ただし、このエラーのためにいきなりDart Sassに移行するのはテスト工数などもかかり業務ではなかなか難しいので、Dart Sassに移行する余裕がある場合に行うのが良いかと思います。

まとめ

  • Incompatible unitsはSassで互換性のない単位の計算をさせようとしている時に発生するエラー。
  • node-sass(LibSass)ではバグがありmin()max()で異なる単位の比較をしようとするとコンパイルができない。
  • いくつかの方法で解決できる。
    • Sassのunquote()を併用する。
    • interpolation(#{""})で文字列としてコンパイルする。
    • Dart Sassに移行する。

このバグに限らず、今後を考えるとDart Sassに移行してしまった方がいいですが、業務ではテストにかかる負担などを考えるとサクッと移行できるものでもないので、緊急性が高い場合はunquote()やinterpolationを使うのが良さそうです。

そして、最近sass-diffというDart SassとNode Sassのコンパイル結果の差を確認するための簡単なWebアプリを作っているので、Sassのコンパイラの違いによる出力の違いをチェックしたい時などに是非ご活用ください。まだ使いにくいところがありますが、今後改善していきます。