いまさらながらダークモード対応に入門した

 こんにちは．
弁護士ドットコムのエンジニアの @blkclct です．

[弁護士ドットコム Advent Calendar 2020](https://qiita.com/advent-calendar/2020/bengo4com) の 15 日目の記事です．

今回はずっと気になっていたけど本腰を入れて調べていなかった Web のダークモード対応について書こうと思います．

# はじめに

昨今，リモートワークが浸透しはじめ，以前にも増してディスプレイをみる時間が増えたように思います．
ぼく自身, ランチ中も iPad で Netflix をみたりしているし，入浴中も YouTube をみてるので 1 日起きている中で 9 割方は何かしら光に晒されている気がしています．（ほんとよくない．．）

なので，眼精疲労対策できることは最低限行いたい気持ちになりますよね．ダークモード対応しているアプリケーションを使えばディスプレイの輝度も抑えられますし，自然光や室内の光の反射による刺激を抑えることができます．

最近は Web でもダークモード対応する流れがよりきているように思います．GitHub も最近[ダークモードに対応](https://twitter.com/github/status/1336362679506784256)しましたし，GitLab も[対応](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28252)しています．業務で長時間眺めるアプリケーションがダークモードだと非常に助かりますよね．最近業務でみたドキュメントもダークモードに対応していて嬉しかった記憶．（※ [Redux Toolkit](https://redux-toolkit.js.org/), [React Hook Form](https://react-hook-form.com/jp/)）

前置きが長くなってしまいましたが，最近よくみる Web でのダークモード対応のやり方を調べたので紹介していきます！

# 🧙‍♀️ 魔法の CSS で 1 発ダークモード対応する

CSS プロパティの [filter](https://developer.mozilla.org/ja/docs/Web/CSS/filter) を使うことでなんとたった 1 行でダークモード対応ができてしまうというものです．

```
:root {
    filter: invert(100%) hue-rotate(180deg);
}
```

![G](https://gyazo.com/a0b617c736532527f4cafcc681f43571/raw)

`invert()`, `hue-rotate()` の 2 つの関数を使うことで配色を反転させたり色相を回転させたりして実現する方法です．

ただ．こちらは画像の要素にも当然，反転がかかってしまうので別途対応したりしないといけなかったりするのでお手軽ではありますが，現実的な方法ではないですね．．

この方法は， [こちらの記事](https://dev.to/dip15739/dark-mode-with-only-1-css-property-17fl)を参考にさせていただきました！ Thanks !

# 🍦 よくあるトグルボタンで切り替えを行う

よくあるトグルでのライトモードとダークモードの切り替えを実際に実装して紹介したいと思います．

調べた感じ，入門程度だと全然敷居が高くありませんでした．ざっくりやるのはこのくらいです．

```
- background, color のプロパティをライドモード，ダークモード用に data 属性 data-thema として用意する
- 切り替え用のトグルボタンを用意する
- JS で data-thema を制御してスタイルを切り替える
```

今回は，Gatsby のスタータキットの [gatsbyjs/gatsby-starter-default](https://github.com/gatsbyjs/gatsby-starter-default) を拡張する形で試しました．

## background, color の CSS を用意する

`:root` を使って，html 全体にデフォルトでは，ライトモードのスタイルを当て，`data-theme="dark"` のときにダークモードのスタイルが当たるように設定します．

```src/components/layout.css
:root {
  background: #E8E6DC;
  color: #3D3D3D;
}

[data-theme="dark"] {
  background: #171923;
  color: #c5cddb;
}

html {
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  font: 112.5%/1.45em georgia, serif, sans-serif;
  box-sizing: border-box;
  overflow-y: scroll;
}
...
```

## 切り替え用のトグルと， data-thema を制御する処理を追加する

次に，トップページにトグルボタンを設置し，ダークモードのオンオフの処理と，data-thema に`dark`をつける処置を追加します．
今回はコンポーネントの分割とか一切せずにそのまま書いてます（雑ですみません🙇‍♂️）

```src/pages/index.js
import React, { useState } from "react"
import { Link } from "gatsby"

import Layout from "../components/layout"
import Image from "../components/image"
import SEO from "../components/seo"

const IndexPage = () => {
  const [isDarkTheme, setIsDarkTheme] = useState(false);
  const handleToggle = e => {
    setIsDarkTheme(e.target.checked);
    if (isDarkTheme) {
      document.documentElement.removeAttribute("data-theme");
    } else document.documentElement.setAttribute("data-theme", "dark");
  };
  return (
    <Layout>
      <SEO title="Home" />
      <label>
        <input
          type="checkbox"
          checked={isDarkTheme}
          onChange={handleToggle}
          style={{ display: `none` }}
        />
        {isDarkTheme ? '🌚' :　'🌞'}
      </label>
      <h1>Hi people</h1>
      <p>Welcome to your new Gatsby site.</p>
      <p>Now go build something great.</p>
      <div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
        <Image />
      </div>
      <Link to="/page-2/">Go to page 2</Link> <br />
      <Link to="/using-typescript/">Go to "Using TypeScript"</Link>
    </Layout>
  )
}

export default IndexPage
```

これだけでダークモードとライトモードの切り替えができるようになります！
![G](https://gyazo.com/4261344bd0157369f1f9022232f8ee61/raw)

全然手前かからなかったのでぜひ試してみてください！また，こちらは 👉 [blkclct/my-default-starter](https://github.com/blkclct/my-default-starter) 今回わたしが実装したソースコードです．

また，CSS の実装は[こちらの記事](https://dev.to/vaishnavme/simple-dark-light-toggle-with-vanillajs-2cbj)を参考にさせていただきました．Thanks !

# 背景色と文字色のコントラスト比を意識しないといけないよという話がある

アクセシビリティの観点から，コントラスト比は意識しないといけないとあります．（※ [Web Content Accessibility Guidelines (WCAG) | W3C](https://www.w3.org/WAI/standards-guidelines/wcag/)）
極端な白黒である `#000`, `#fff` でコンストラスト比が最大値である `21` になります．コントラスト比が低すぎると背景と文字が同化して見えにくくなる感じですね． 逆に高すぎると，白が強調されすぎて眩しくみえてしまうというわけですね．
アクセシビリティ的には，文字数が `14px` 付近だとコントラスト比は `7` 以上欲しいという感じのようです．

[こちらの記事](https://blog.karenying.com/posts/github-darkmode-sucks)で，Spotify や Facebook, YouTube のコンストラスト比をあげてくれているのですが，個人的には，Facebook が一番目に馴染む気がします．なので `14` 付近が素敵かなと．

# さいごに

今回は全くの無知からダークモード対応の世界に触れてみました．とても楽しかったです！次は SSR でダークモード対応をやってみようと思います！

これからもリモートワークが続いていくと思います．やはり，目が一番大事かと思いますので眼精疲労対策しっかりやっていきましょう！

ちなみに，ぼくのお気に入りの目薬は，ほどよい刺激と抜群の差しやすさをもつ[サンテのメディカル12](https://www.santen.co.jp/ja/healthcare/eye/products/otc/sante_medical/product/medical_12.jsp) です．また，[ホットアイマスク](https://www.kao.co.jp/megrhythm/eye/)のすきな香りはゆずです．

最後まで読んでくださりありがとうございました．

そして，明日の 16 日目の投稿は， @ysy-g さんです．お楽しみに！