あんまり手間をかけない、なんちゃってダークモードUIの作り方

## これは何

- Dark ModeのUIを「厳密ではないけど手間をかけずにそれっぽく作る」ためのTipsを書いた記事です
- あくまで**なんちゃって**です、ご了承ください
    - 本当は手を入れないといけない箇所が色々あります
    - 暗い背景にあわせてメインカラーの明度や彩度を調整するとか
    - Light Modeで`box-shadow`を使ってelevationを表現している箇所は、`box-shadow`をやめてsurfaceの色の違いelevationを表現するとか

## 完成品

GitHub Pagesで公開しました。
ページを開いた上で、システムの設定でLightとDarkを入れ替えて見てください。
リロード無しでUIが切り替わります。

https://xrxoxcxox.github.io/qiita-simplified-dark-mode/

スクリーンショットも載せておきます。

| Light Mode | Dark Mode |
| --- | --- |
| ![白い背景に黒い文字の画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e949b33a-adf7-9568-fcd7-e38a40e95102.png) | ![黒い背景に白い文字の画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/7504e714-64b5-d29c-1f6c-c9bfa5004cd6.png) |

## おおまかな仕組み

`@media (prefers-color-scheme: dark)`とCSS Custom properties（CSS変数）を組み合わせます。

`@media (prefers-color-scheme: dark)`はメディアクエリの一種で、ユーザーがシステム設定でLight ModeをDark Modeのどちらを選んでいるかを判別できます。
そして、予めCSS変数で定義しておいた色だけを使ってスタイリングをすれば1つのコードでLightとDarkどちらのUIも組めるようになる、という仕組みです。

## 実際のコード

[全てのコードはGitHubをご覧ください。](https://github.com/xrxoxcxox/qiita-simplified-dark-mode)
この記事の中ではCSSだけにフォーカスします。

### CSS変数で使う色をひとしきり定義する

`:root`要素の中でCSS変数を定義すると、ページ内のどこでも使えるようになります。
通常用（=Light Mode）のパレットと、Dark Mode用のパレットをそれぞれ定義しています。

```css:style.css
:root {
  --text-high-emphasis: rgba(0 0 0 / 0.87);
  --text-medium-emphasis: rgba(0 0 0 / 0.6);
  --text-on-primary: rgba(255 255 255 / 0.87);
  --primary: #0d47a1;
  --background: #f6f6f6;
  --surface: #fff;
}

@media (prefers-color-scheme: dark) {
  :root {
    --text-high-emphasis: rgba(255 255 255 / 0.87);
    --text-medium-emphasis: rgba(255 255 255 / 0.6);
    --text-on-primary: rgba(0 0 0 / 0.87);
    --primary: #ffca28;
    --background: #212121;
    --surface: #2f2f2f;
  }
}
```

#### Sassの変数ではダメ？

スタイルを組む上ではSassの変数の方が馴染み深い方も多いかもしれませんが、それではNGです。
このようにコードを書いたとして、Light Mode用の色が適用されることはありません。

```scss
:root {
  $text-high-emphasis: rgba(0 0 0 / 0.87) !global;
  $text-medium-emphasis: rgba(0 0 0 / 0.6) !global;
  $text-on-primary: rgba(255 255 255 / 0.87) !global;
  $primary: #0d47a1 !global;
  $background: #f6f6f6 !global;
  $surface: #fff !global;
  
  @media (prefers-color-scheme: dark) {
    $text-high-emphasis: rgba(255 255 255 / 0.87) !global;
    $text-medium-emphasis: rgba(255 255 255 / 0.6) !global;
    $text-on-primary: rgba(0 0 0 / 0.87) !global;
    $primary: #ffca28 !global;
    $background: #212121 !global;
    $surface: #2f2f2f !global
  }
}
```

Sassの変数はあくまで「Sass上では同じ値として繰り返し使える」存在です。
CSSに変換された後は動的に値を変更できないため、後に書いてある`(prefers-color-scheme: dark)`のルールしか適用されません。
（もちろん、darkを先、lightを後と順番を変えて書いたらlightのルールしか適用されません。）

### 定義したCSS変数でスタイルを組む

あとは、定義したCSS変数を使ってコードを書いていくだけです。
普通のCSSを書くのとほとんど変わりません。

```css:実際のコードから一部抜粋
html {
  background-color: var(--background);
  color: var(--text-high-emphasis);
}

.main {
  background-color: var(--surface);
  border-radius: 16px;
  padding: 32px;
  width: min(640px, 100%);
}

.note {
  color: var(--text-medium-emphasis);
  font-size: 12px;
  margin-top: 16px;
}

.button {
  background-color: var(--primary);
  border-radius: 8px;
  color: var(--text-on-primary);
  font-size: 18px;
  font-weight: 700;
  margin-top: 32px;
  padding: 12px 16px;
}
```

`var(--somecolor)`の書式になっている箇所がCSS変数を用いている箇所です。
Dark Modeのためだけのコードは1行も書いていません。

#### カラーシステムの設計

ちなみに本筋からは外れますが、色の設計をするときはMaterial Designのcolor systemを参考にすると分かりやすいと思います。

https://material.io/design/color/the-color-system.html#color-theme-creation

- 背景色（Background）
- 要素の表面の色（Surface）
- メインの色（Primary）

といったように考えることで、そもそも秩序ある配色でプロダクトを作れるようになります。
これはCSSだけの話ではなく、UIのモックアップ作成のときから考えておけると良い項目です。

## まとめ

- `@media (prefers-color-scheme: dark)`とCSS Custom properties（CSS変数）を組み合わせる
    - Sassの変数ではNG
- 定義した色を用いてスタイリングをする
    - Material Designのcolor systemの考え方を取り入れると分かりやすい