ボタンの通常時・ホバー時・押したときの色、薄い背景色、境界線の色。こうしたバリエーションを毎回カラーピッカーで選んでいると、色の数だけ変数が増え、あとからテーマカラーを変えるのが大仕事になります。
Sass の sass:color モジュールを使えば、ベースの色を1つ決めるだけで、明るい色や暗い色を計算で作り出せます。ベースを変えれば、派生する色もすべて自動で変わります。
この記事では、color.scale()・color.adjust()・color.change() の違いと使い分け、色を混ぜる color.mix()、そして非推奨になった lighten() や darken() からの移行方法までを解説します。
目次
まずは @use で読み込む
色に関する関数は sass:color という組み込みモジュールにまとまっています。使うファイルの先頭で @use します。
@use "sass:color";
$base: #007bff;
.button {
background-color: $base;
&:hover {
// 15% だけ暗い色をその場で作る
background-color: color.scale($base, $lightness: -15%);
}
}
.button {
background-color: #007bff;
}
.button:hover {
background-color: #0069d9;
}
計算はコンパイル時に行われるので、出力されるのは普通の色コードです。ブラウザー側で計算が走ることはありません。
下のデモは、#007bff という1色から color.scale() で作った配色です。3つの色見本と、ホバー・クリックで色が変わるボタンが、すべて同じベース色から計算されています。
<div class="palette">
<div class="swatch swatch--light">
<span>scale +40%</span>
</div>
<div class="swatch swatch--base">
<span>ベースの色</span>
</div>
<div class="swatch swatch--dark">
<span>scale -25%</span>
</div>
</div>
<button class="button" type="button">ホバー・クリックしてみてください</button>
/* Sass の color.scale() が生成した CSS。ベースは #007bff の1色だけ */
.palette {
display: flex;
gap: 8px;
margin-bottom: 20px;
}
.swatch {
flex: 1;
padding: 24px 8px;
border-radius: 6px;
text-align: center;
font-size: 12px;
color: #fff;
}
.swatch--light {
background-color: #66b0ff; /* color.scale($base, $lightness: 40%) */
color: #0a2540;
}
.swatch--base {
background-color: #007bff; /* ベースの色 */
}
.swatch--dark {
background-color: #005cbf; /* color.scale($base, $lightness: -25%) */
}
.button {
background-color: #007bff;
color: #fff;
border: none;
border-radius: 4px;
padding: 10px 24px;
cursor: pointer;
transition: background-color 0.2s;
}
.button:hover {
background-color: #0069d9; /* color.scale($base, $lightness: -15%) */
}
.button:active {
background-color: #005cbf; /* color.scale($base, $lightness: -25%) */
}
color.scale と color.adjust の違い
明るさを変える関数は2つあり、この違いを理解しておくことが sass:color を使いこなす鍵になります。どちらも $lightness や $saturation といったキーワード引数を受け取りますが、値の解釈が違います。
| 関数 | 動き | 明度 90% の色に +10% した場合 |
|---|---|---|
color.adjust() | 指定した分だけ値を足し引きする | 100%(真っ白になる) |
color.scale() | 上限までの残りに対する割合で変化させる | 91%(少しだけ明るくなる) |
color.change() | 指定した値に置き換える | 10%(指定した値そのもの) |
実際に試してみます。もともと明るい色に対して、それぞれを適用した結果です。
@use "sass:color";
// 明度が 90% 台の、もともと明るい色
$pale: #f8d7da;
.a { background-color: color.adjust($pale, $lightness: 10%); }
.b { background-color: color.scale($pale, $lightness: 10%); }
.a {
/* 90% + 10% = 100% で真っ白になってしまう */
background-color: #ffffff;
}
.b {
/* 残り 10% のうちの 10% ぶんだけ明るくなる */
background-color: #f9dbde;
}
color.adjust() は単純な足し算なので、明るい色に大きな値を足すと簡単に真っ白になります。逆に暗い色から引けば真っ黒になります。
color.scale() は「上限までの残りに対する割合」で変化するため、どれだけ繰り返しても白や黒に振り切れません。複数の色に同じ処理をかけたいときは color.scale() のほうが安全です。テーマカラーが明るい色に差し替えられても、ホバー色が真っ白になって見えなくなる、という事故が起きません。
使い分けの目安はこうなります。値をきっちり指定したいときは color.change()、決まった分だけ動かしたいときは color.adjust()、ベース色が何であっても破綻させたくないときは color.scale() です。実務で最もよく使うのは color.scale() でしょう。
調整できる項目
3つの関数はいずれも、明度だけでなく彩度や不透明度もキーワード引数で指定できます。複数を同時に渡すこともできます。
@use "sass:color";
$base: #007bff;
// 彩度を落として、少し暗くする(落ち着いた色になる)
.muted {
color: color.scale($base, $saturation: -40%, $lightness: -10%);
}
// 不透明度だけを変える(半透明の背景に使う)
.overlay {
background-color: color.change($base, $alpha: 0.15);
}
// 色相を回して、対になる色を作る
.accent {
color: color.adjust($base, $hue: 180deg);
}
| 引数 | 意味 |
|---|---|
$lightness | 明るさ。プラスで明るく、マイナスで暗く |
$saturation | 鮮やかさ。マイナスにすると灰色に近づく |
$hue | 色相。deg 単位で色相環を回す |
$alpha | 不透明度。0 で完全に透明、1 で不透明 |
$alpha を変えた色は、出力時に rgba() の形になります。半透明のオーバーレイやホバー時の淡い背景色を作るときに便利です。
2色を混ぜる color.mix
「テーマカラーを白で薄めた背景色がほしい」というときは color.mix() が使えます。第3引数の $weight は、第1引数の色をどれだけの割合で混ぜるかです。
@use "sass:color";
$base: #007bff;
.alert {
// ベース色を 12% だけ混ぜた、ごく淡い背景
background-color: color.mix($base, #fff, 12%);
// ベース色を 40% 混ぜた境界線
border: 1px solid color.mix($base, #fff, 40%);
color: color.scale($base, $lightness: -30%);
}
この書き方なら、$base を差し替えるだけで、アラートの背景・枠線・文字色が一式そろいます。成功・警告・エラーの3種類を @each で回して生成する、といった使い方にもそのまま応用できます。
色から値を取り出す
色の明るさに応じて文字色を白か黒に切り替えたい、といった判定をしたいときは、色の成分を取り出して比較します。現在の Dart Sass では color.channel() を使います。
@use "sass:color";
// 背景色に対して読みやすい文字色を返す
@function text-color($bg) {
@if color.channel($bg, "lightness", $space: hsl) > 60% {
@return #222;
} @else {
@return #fff;
}
}
.badge {
background-color: #ffd43b;
color: text-color(#ffd43b); // → #222
}
古い記事では color.lightness($bg) や lightness($bg) という書き方が紹介されていますが、こうした成分ごとの関数は新しい Dart Sass では非推奨になりました。これから書くなら color.channel() を使ってください。
lighten() や darken() からの移行
Sass の色調整といえば lighten() と darken() を思い浮かべる方も多いはずです。これらは古い記事や既存のプロジェクトで今でもよく見かけますが、現在は非推奨で、color.adjust() または color.scale() への置き換えが案内されています。
| 古い書き方 | 置き換え先 |
|---|---|
lighten($c, 10%) | color.adjust($c, $lightness: 10%) |
darken($c, 10%) | color.adjust($c, $lightness: -10%) |
saturate($c, 20%) | color.adjust($c, $saturation: 20%) |
transparentize($c, 0.5) | color.adjust($c, $alpha: -0.5) |
mix($a, $b, 30%) | color.mix($a, $b, 30%) |
そのまま機械的に置き換えるなら color.adjust() ですが、前述のとおり明るい色・暗い色で破綻しやすい性質があります。移行を機に、見た目を確認しながら color.scale() に寄せていくほうが扱いやすくなります。
エラーや警告が出るとき
Undefined function と言われる
@use "sass:color"; を書き忘れていないか確認してください。モジュールの関数は、読み込んだファイルの中でしか使えません。他のファイルで読み込んでいても、そのファイル自身が @use していなければ使えない点に注意が必要です。
名前空間を付け忘れて scale(...) と書いているケースもあります。color. を前に付けるのを忘れないでください。
deprecated という警告がたくさん出る
ビルド時に非推奨の警告が並ぶ場合、古い関数(lighten() など)がどこかに残っています。警告にはファイル名と行番号が含まれているので、上の対応表を見ながら順に置き換えていけば消えます。
自分のコードではなく、読み込んでいるライブラリ側が原因のこともあります。その場合はライブラリの更新を待つか、バージョンを上げて対応済みのものを使ってください。
CSS のカスタムプロパティには使えない
color.scale(var(--color-primary), $lightness: -15%) のような書き方はできません。Sass はコンパイル時に計算するため、var() の中身を知ることができないからです。
カスタムプロパティで実行時に色を切り替えたい場合は、Sass 側で必要なバリエーションをすべて生成して :root に書き出しておくか、CSS の color-mix() を使ってブラウザー側で計算させてください。
まとめ
sass:color モジュールを使うと、ベースの色から明るい色・暗い色・淡い背景色を計算で作り出せます。使うときは @use "sass:color"; を忘れずに書いてください。
中心になるのは3つの関数です。値を置き換える color.change()、決まった分だけ足し引きする color.adjust()、上限までの残りに対する割合で変化させる color.scale()。どんなベース色でも破綻させたくないなら color.scale() を選ぶ、と覚えておけば失敗しません。
lighten() や darken() は非推奨になっているので、これから書くコードでは使わないようにしましょう。色の定義を1か所にまとめておけば、テーマカラーの変更が変数1行の書き換えで済むようになります。