WebページもOSのDark Modeに対応できる

この記事は[ニフティグループ Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nifty)の11日目の記事です。
昨日は @iNakamura の「[自然言語処理でツイートがバズるかを予測しようとした話](https://qiita.com/iNakamura/items/ae24c9f511bea4ae19cb)」でした！
大学時代に、TwitterAPIの制限にぶつかって嘆いていた頃を思い出しますね。

---

# はじめに
![darkmode_switch.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225997/36a742d4-07a3-ccb0-8439-e3b77d612c6b.gif)
ソースコード: https://codepen.io/rodhamjun/pen/rNaLGLW

さて、今回はWebページもOSのDark Modeと連動できるようになったという話です。
最近、様々なOSが次々とダークモードに対応して、いろんな会社がその対応に追われていますね。
そんな中、ついこの間の[Chrome Dev Summit 2019](https://youtu.be/-oyeaIirVC0?t=336)の動画を見ていたら、Webサイトでもダークモードに対応できるようになっていた事を知り、やり方もとてもシンプルだったので共有したいと思いました。

# ダークモードに対応する
```css
@media (prefers-color-scheme: dark) {
  body {
    background-color: #000;
    color: #fff;
  }
}
```
これだけです。
レスポンシブデザインでおなじみの`@media` メディアクエリを使いますね。
この構文で、OSの設定でダークモードがオンになっている場合は、背景を黒にして、文字を白にします。

`prefers-color-scheme` で指定できる値は以下の3つです。

- `no-preference`
- `dark`
- `light`

# ブラウザ対応状況
現時点では世界人口の76%のブラウザはすでに対応していて、十分普及が進んでいる状況です。
<img width="1270" alt="スクリーンショット 2019-12-08 21.26.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225997/90d37633-78fb-373b-3adc-2bb94df856ce.png">
https://caniuse.com/#feat=prefers-color-scheme

# Chrome Dev Tools でオンオフを切り替える

※ Chrome Canary
![chome-dev-tools-dark-mode.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225997/74d3b6ff-89de-a9d0-152f-656214cc13b4.gif)

Chrome 現行ビルドではまだ、対応していませんが、Chrome Canary (早期試用版) を使えば、開発者用ツールでDark Modeのオンオフを切り替えることができます。
将来的には、通常盤でもこれができるようになる予定です。

`・・・` → `More Tools` → `Rendering` → `prefers-color-scheme`

# Dark Mode 対応する時に使えるテクニック

## JavaScriptでDark Modeがオンかどうか確認する

```javascript
if (matchMedia('(prefers-color-scheme: dark)').matches) {
  // OS の dark mode がオンになっています
} else {
  // OS の dark mode がオフになっています
}
```
JavaScriptでオンオフを確認するときも、メディアクエリを使います。
既にサイトで Dark Mode のを提供していて、これをOSの設定と連動させるようにしたい場合はこれを使うと良いでしょう。


## CSS変数を使う

```css
@media (prefers-color-scheme: light) {
  --background-color: white;
  --font-color: black;
  --primary-color: blue;
}

@media (prefers-color-scheme: dark) {
  --background-color: black;
  --font-color: white;
  --primary-color: darkblue;
}

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

a {
  color: var(--primary-color);
}
```
light と dark の場合に分けて、同じCSSを２度書くのは管理が大変です。
そんなときに便利なのが、CSS変数です。
ブラウザの対応状況を確認し、合わせて使ってみてください。

## 画像の彩度を落とす

```css
img {
  filter: grayscale(30%);
}
```

背景を暗くすると、画像が浮いてしまいます。
これを解決する方法として、grayscaleを下げるとよいそうです。
参考： https://medium.com/dev-channel/re-colorization-for-dark-mode-19e2e17b584b


## SVGアイコンを色反転
```css
img[src*=svg] {
  filter: invert(100%) grayscale(30%);
}
```
黒いsvgアイコンのアイコンは色反転すると、二つ作る必要がありません。

## 画像を差し替える

```html
<picture>
    <source srcset="night.jpg" media="(prefers-color-scheme: dark)">
    <img src="day.jpg">
</picture>
```
色反転では対応できないような場合は、
picture タグを使うと、imgタグのsrc画像を差し替えることができます。
参考: https://stackoverflow.com/questions/55787167/how-to-change-image-source-based-on-css-prefers-color-scheme

# その他の類似メディアクエリ
今回の、 `prefers-color-scheme` 以外にも、たくさんのアクセシビリティ関連のメディアクエリができました。
アクセシビリティに配慮しているサイトの場合、OSの設定にあわせて、これらで設定のオンオフを切り替えられるようになります。合わせて、確認ください。

- `inverted-colors:` 色反転の設定
- `prefers-reduced-motion:` 視差効果を減らす設定
- `prefers-reduced-transparency:` 透過表現を減らす設定
- `prefers-contrast:` コントラストモードの設定
- `forced-colors:` 強制色指定の設定

# 最後に
いかがだったでしょうか？

6月の WWDC2019 でiOS 13がダークモードに対応して、会社の先輩が「Webサイトもダークモードに対応しない時代が来るのかな・・・。」と言っていたことを思い出します。

他にも、Chrome Dev Summit 2019の動画はたくさんあります。
他にも、[フォーム要素のデザインがリデザインされ、セレクトボックスでアイコンが使えるようになる話](https://youtu.be/ZFvPLrKZywA)もありました。
気になる方は以下のリンクで、公演の一覧をチェックしてみてください。
https://www.youtube.com/watch?v=F1UP7wRCPH8&list=PLNYkxOF6rcIDA1uGhqy45bqlul0VcvKMr

---

明日は、@mito1296kのAnsible関係の記事です。お楽しみに！