CSS変数とSass(Scss)変数を利用してダークモードになった時の配色などを自動で変更することができます。
例えばテーマボタンをクリックしたら<html>
にJavaScriptでdata-theme="dark"
という属性を追加し、そのdata属性を判定してダークモードの切り替えを実装する場合のCSS&Sassの設定を紹介します。
基本的なダークモードの設定方法
まずはライトモード(通常表示)用の$theme-colors
とダークモード用の$dark-theme-colors
というSass変数に配列で各カラーコードを定義していきます。
そしてSass機能の@each
文で$theme-colors
を:root {}
内にループ処理で出力していきます。
出力結果は--color-primary
、--color-secondary
のようにCSS変数で出力され、ダークモードの時は&[data-theme='dark'] {}
内のカラーが適応されます。
/* Sass Variables */
// Light Theme Colors
$theme-colors: (
primary: #333333,
secondary: #cccccc,
success: #16b43b,
warning: #f8c11a,
danger: #f13245,
background: #e9e9e9,
base: #f7f7f7,
);
// Dark Theme Colors
$dark-theme-colors: (
primary: #f7f7f7,
secondary: #cccccc,
success: #33e65d,
warning: #f1c339,
danger: #f74254,
background: #1a1a1a,
base: #333333,
);
/* CSS Variables */
:root {
// Light Mode
@each $key, $color in $theme-colors {
--color-#{$key}: #{$color};
}
// Dark Mode
&[data-theme='dark'] {
@each $key, $color in $dark-theme-colors {
--color-#{$key}: #{$color};
}
}
}
これで基本的なダークモード対応したCSS設定は完了です。
var(--color-primary)
のようにCSS変数をvar()
関数で呼び出すことで、テーマに対応したカラーコードが自動で反映されます。
.wrapper {
background-color: var(--color-wrapper);
}
.headding {
color: var(--color-primary);
}
RGBAでカラー設定できない問題
上記の記述だとテーマごとにカラーを切り替えることは可能ですが、CSS変数だとRGBAで透過させたい時にrgba(var(--color-background, 0.5));
のような記述は動作しません。
.overlay {
// 動かない
background-color: rgba(var(--color-background));
}
なぜrgba()関数が使えないのか
Sass環境だとrgba(#000000, 0.5);
のように記述することができますが、
これはSass側がrgba()
プロパティにHEX形式(#000000)の引数が入ったら自動でRGB形式(0, 0, 0)に変換してCSSにコンパイルしているから使用できるのです。
公式 Sass: color
しかし、CSS変数を呼び出すvar()
関数はSassがCSSにコンパイルした後にしか動作しないので、var()
関数をコンパイル前に読み込んでもrgba()
に入った値はHEX形式からRGB形式に変換できません。
var() 関数は、プロパティ名、セレクター、またはプロパティ値以外のところでは使用できません。
(使用してしまうと、無効な構文が生成されるか、もしくはその変数に接続していない値が生成されてしまいます。)
引用:MDN
なのでrgba(var(--color-background, 0.5));
のように記述してもSassがrgba()関数内でvar()関数を理解できないのでコンパイルできません。
(プロパティを使用していないvar()
関数は使用できます)
.overlay {
// 動かない(rgbaプロパティを使用している)
background-color: rgba(var(--color-background), 0.5);
// 動く(プロパティを使用してない)
background-color: var(--color-background);
}
しかし、このままだとrgba()
プロパティが使えないので、アルファを指定した透過処理ができなくなってしまいます。
それでもRGB形式のカラーコードは管理したくない
RGB形式のカラーコードは255, 255, 255
や0, 0, 0,
のように3つ数字で構成されています。
これをそのままSass変数の$theme-colors
に設定しても良いんですが、デザインツール(Illustrator、Photoshop、Sketch、Figmaなど)を使用する場合HEX形式でカラーコードを取得することが多いので、新しいカラーを追加するにはデザインツール側でRGB形式に変換や取得する必要があります。
// デザインツール側でRGB形式のカラーコードを取得して追加する
$theme-colors: (
primary: 51, 51, 51, // #333333
secondary: 204, 204, 204, // #cccccc
success: 22, 180, 59, // #16b43b
warning: 248, 193, 26, // #f8c11a
danger: 241, 50, 69, // #f13245
background: 233, 233, 233, // #e9e9e9
wrapper: 247, 247, 247, // #f7f7f7
);
これだとカラーコードだけで色の想像つきにくく、Visual Studio Codeエディタなどでカラー補完が効かない為、配色の管理が難しくなります。
(エディタや拡張機能によってはカラー補完が効く場合があります)
Sass環境でもCSS変数でrgba()プロパティを利用する為には?
上記を踏まえてSass変数はHEX形式のままでCSS変数はRGB形式にしたいですね。
それを解決するためにはSass側でHEX形式のカラーコードをCSSにコンパイルする前にRGB形式に変換することで解決します。
- Sass変数を定義(HEX形式)
- Sass変数をRGB形式に変換 // ←ここ
- CSS変数にSASS変数(RGB形式)を格納
- CSS変数をvar()で呼び出す
HEX形式のカラーコードをRGB形式に変換するにはSassの@function
機能で関数化するのが手っ取り早いです。
公式 Sass: @function
hexToRGB()関数を作成する
まずはhexToRGB()
という関数を作成します。
これはhexToRGB()
にHEX形式の値を渡したらred(), green(), blue()
に分解しRGB形式のカラーコードに変換します。
そして:root{}
内で#{hexToRGB($color)}
で関数を呼び出すことで、Sassがコンパイルする前にRGB形式のカラーコードをCSS変数に出力します。
// HEX形式のカラーコードをRGBに変換する
@function hexToRGB($hex) {
@return red($hex), green($hex), blue($hex);
}
:root {
@each $key, $color in $theme-colors {
--color-#{$key}: #{hexToRGB($color)}; // hexToRGB()関数を呼び出す
}
&[data-theme='dark'] {
@each $key, $color in $dark-theme-colors {
--color-#{$key}: #{hexToRGB($color)}; // hexToRGB()関数を呼び出す
}
}
}
これでrgb()
プロパティでCSS変数を呼び出すことができますが、呼び出すごとにrgba(var(--color), 0.5)
のような長い記述をしなければいません。
// alpha値が0.5(透過0.5)
.overlay {
background-color: rgba(var(--color-background), 0.5);
}
// alpha値が1(透過なし)
.overlay {
background-color: rgba(var(--color-background), 1);
}
// rgba()がなくても大丈夫
.overlay {
background-color: var(--color-background);
}
getColor()関数を作成する
rgba()
プロパティを使うたびに毎回rgba(var(--color), 0.5)
と記述するのはめんどくさいので、カラーコード取得用のgetColor()
という関数を作成します。
@function hexToRGB($hex) {
@return red($hex), green($hex), blue($hex);
}
// CSS変数をRGBA関数で使えるようにする
@function getColor($color_name, $alpha: 1) { // $alphaはデフォルト値を入れておく(透明度)
@return rgba(var(#{$color_name}), $alpha);
}
これでgetColor()
を呼び出すだけで@function
関数が実行され内部的にrgba(var(--color-background), 0.5)
の形式に変換してくれます。
// alpha値が0.5(透過0.5)
.overlay {
background-color: getColor(--color-background, 0.5);
}
// alpha値が1(透過なし)
.overlay {
background-color: getColor(--color-background, 1);
}
// rgba()がなくても大丈夫
.overlay {
background-color: var(--color-background);
}
完成
/* Functions */
// HEX形式のカラーコードをRGBに変換する
@function hexToRGB($hex) {
@return red($hex), green($hex), blue($hex);
}
// CSS変数をRGBA関数で使えるようにする
@function getColor($color_name, $alpha: 1) {
@return rgba(var(#{$color_name}), $alpha);
}
/* Sass Variables */
// Light Theme Colors
$theme-colors: (
primary: #333333,
secondary: #cccccc,
success: #16b43b,
warning: #f8c11a,
danger: #f13245,
background: #e9e9e9,
base: #f7f7f7,
);
// Dark Theme Colors
$dark-theme-colors: (
primary: #f7f7f7,
secondary: #cccccc,
success: #33e65d,
warning: #f1c339,
danger: #f74254,
background: #1a1a1a,
base: #333333,
);
/* CSS Variables */
:root {
@each $key, $color in $theme-colors {
--color-#{$key}: #{hexToRGB($color)};
}
&[data-theme='dark'] {
@each $key, $color in $dark-theme-colors {
--color-#{$key}: #{hexToRGB($color)};
}
}
}