Produced by FOURIER

SASS(SCSS)とCSS変数を連携してダークモードを実装

HirayamaHirayama calender 2022.10.27

はじめに

Webサイトを作る上でCSSは避けては通れませんが、CSSをわかりやすく書くことは難しく、作っていくうちに複雑になりすぎることがよくあります。

また、JavaScriptを使用して動的にページの見た目を変えることもよくあり、JavaScriptとも連携しやすいように書く必要があります。

この2つの問題はそれぞれSASS(SCSS)CSSカスタムプロパティで解決できるので、本記事では具体例としてダークモードの実装方法について解説したいと思います。

なお、本記事ではSASSSCSSの文法で記述します。

簡単な例

まずはSASSの変数機能とCSSカスタムプロパティを使ってダークモードを実装します。

// 色をSCSS変数として定義する
$border-color-light: #000000;
$border-color-dark: #ffffff;
$text-color-light: #000000;
$text-color-dark: #ffffff;
$background-color-light: #ffffff;
$background-color-dark: #000000;

// テーマごとに変数を設定
.light-theme {
  --border-color: $border-color-light;
  --text-color: $text-color-light;
  --background-color: $background-color-light;
}

.dark-theme {
  --border-color: $border-color-dark;
  --text-color: $text-color-dark;
  --background-color: $background-color-dark;
}

body {
  background-color: var(--background-color);

  button {
    // CSSカスタムプロパティの値を使うように設定
    border-color: var(--border-color);
    color: var(--text-color);
    background-color: var(--background-color);
  }
}

これはコンパイルすると、以下のようなCSSになり、light-themedark-themeクラスが定義され、それぞれ同じCSSカスタムプロパティが定義されています。

.light-theme {
    --border-color: #000000;
    --text-color: #000000;
    --background-color: #ffffff;
}

.dark-theme {
    --border-color: #ffffff;
    --text-color: #ffffff;
    --background-color: #000000;
}

body {
  background-color: var(--background-color);
}

body button {
    border-color: var(--border-color);
    color: var(--text-color);
    background-color: var(--background-color);
}

生成されたlight-themedark-themeクラスをJavaScriptで切り替えることで、同じ名前のCSSカスタムプロパティでもテーマごとに値が切り替わり、ダークモードの実装が実現できます。

See the Pen spectrum-web-analyzer by FOURIER Inc. (@fourier-inc) on CodePen.

値の生成

上記の例でも目的は達成できていますが、テーマごとにSASS変数を定義し、CSSカスタムプロパティとして定義する必要があるため、記述するのが大変です。 そのため、SASSの機能を活用して、その手間を減らします。

$schemes: light, dark;

$scheme-colors: (
  border: (#000000, #ffffff), //1番目がライトテーマ、2番目がダークテーマ
  text: (#000000, #ffffff),
  background: (#ffffff, #000000)
);

@each $scheme in $schemes {
  // テーマごとのクラスを作成
  .#{$scheme}-theme {
    // 色を定義
    @each $name, $colors in $scheme-colors {
      $color: nth($colors, index($schemes, $scheme));
      --#{$name}-color: #{$color};
    }
  }
}

main {
  button {
    border-color: var(--border-color);
    color: var(--text-color);
    background-color: var(--background-color);
  }
}

少しコードが複雑になりましたが、生成されるCSSは同じです。このコードでは複数のSASSの機能を使用しています。

1つ目は$scheme-colorsのマップ変数で、bordertextの名前をキーとして、色を配列にして1番目にライトテーマ用、2番目にダークテーマ用の色をいれています。 こうすることで、テーマごとに同じ名前の変数を定義する必要がなくなり、コード量が減って楽になります。

2つ目は@eachを使って$scheme$scheme-colorsの変数を順番に取り出している点で、この部分でCSSカスタムプロパティを同じように一気に定義しています。

このように書くことで、新しく色を追加したい場合でも最小限の手間で追加することが可能となりました。

RGB値の変数を定義

ここまででテーマごとの色の定義方法について解説しましたが、これらの値には透過値が入っておらず、後から透過値を組み合わせることができない問題があります。

そのため、透過値と組み合わせられるようにRGB値を定義します。

$schemes: light, dark;

$scheme-colors: (
  border: (#000000, #ffffff), //1番目がライトテーマ、2番目がダークテーマ
  text: (#000000, #ffffff),
  background: (#ffffff, #000000)
);

@each $scheme in $schemes {
  // テーマごとのクラスを作成
  .#{$scheme}-theme {
    // 色を定義
    @each $name, $colors in $scheme-colors {
      $color: nth($colors, index($schemes, $scheme));
      $r: red($color);
      $g: green($color);
      $b: blue($color);
      --#{$name}-color: #{$color};
      --#{$name}-r-color: #{$r};
      --#{$name}-g-color: #{$g};
      --#{$name}-b-color: #{$b};
      --#{$name}-rgb-color: #{$r, $g, $b};
    }
  }
}

main {
  button {
    border-color: var(--border-color);
    color: var(--text-color);
    background-color: var(--background-color);
  }
}

これをコンパイルすると、以下のようになります。

.light-theme {
    --border-color: #000000;
    --border-r-color: 0;
    --border-g-color: 0;
    --border-b-color: 0;
    --border-rgb-color: 0, 0, 0;
    --text-color: #000000;
    --text-r-color: 0;
    --text-g-color: 0;
    --text-b-color: 0;
    --text-rgb-color: 0, 0, 0;
    --background-color: #ffffff;
    --background-r-color: 255;
    --background-g-color: 255;
    --background-b-color: 255;
    --background-rgb-color: 255, 255, 255;
}

.dark-theme {
    --border-color: #ffffff;
    --border-r-color: 255;
    --border-g-color: 255;
    --border-b-color: 255;
    --border-rgb-color: 255, 255, 255;
    --text-color: #ffffff;
    --text-r-color: 255;
    --text-g-color: 255;
    --text-b-color: 255;
    --text-rgb-color: 255, 255, 255;
    --background-color: #000000;
    --background-r-color: 0;
    --background-g-color: 0;
    --background-b-color: 0;
    --background-rgb-color: 0, 0, 0;
}

main button {
    border-color: var(--border-color);
    color: var(--text-color);
    background-color: var(--background-color);
}

CSSカスタムプロパティが大量に定義されましたが、これで各色のRGB値が使用できるようになりました。

特徴的なのが—#{name}-rgb-color変数で、値の中にカンマが入っています。このカンマ入りのCSSカスタムプロパティは、以下のようにCSSrgbaと組み合わせて使用することで透過値を設定できます。

div {
  background: rgba(var(--background-rgb-color), .1);
}

まとめ

本記事ではSASSCSSカスタムプロパティを連携してダークテーマの実装をしました。 SASSを使うことで素のCSSよりも簡潔に書くことができ、CSSカスタムプロパティで動的な要素も比較的簡単に実装できました。

この記事で紹介した機能は様々な形で応用できるので、読者の皆様の参考になれば幸いです。

新しいメンバーを募集しています

Hirayama

Hirayama / Engineer

1997年生まれ、南伊豆出身。学生時代にC#で画像処理アプリケーションを作ったりしていました。業務では主にLaravelを使用してサーバーサイドのプログラミングをしています。趣味はドライブとシミュレーションゲーム。