2019-12-25

tobijibu

にほんご。をダークモードに対応した

先日、にほんご。にダークモードを追加しました。 モノクロベースのサイトなのであまり労力をかけずに対応することができました。

ダークモードはCSSのprefers-color-schemeを利用して、OSのテーマから判定するパターンが多いようですが、 にほんご。ではprefers-color-schemeを使わず、サイト上で切り替えてもらうという実装にしました。 対応していないブラウザを利用しているユーザーや、Windows7以下を利用しているユーザーも多いため、 そういう環境のユーザーでもモード選択できるような環境にしておきたいと思い、この方法をとりました。

今回はその実装方法を紹介したいと思います。

まずはページ作成

まずは適当にページを作成します。

linkタグを2つ用意しました。 1つはmain.cssを読み込みます。こちらはページのレイアウトだったり、文字サイズ等、テーマ(色)とは関係無いスタイルを定義します。 2つめはテーマを指定するためのタグです。このタグにはJavaScriptでCSSを判定させます。そのためJSで操作しやすいようにidを設定しておきます。 ついでにhrefを空にしてあります。

headタグ内のscriptにはテーマ切り替え用スクリプトを定義します。これについては後ほど解説します。

HTML
<!DOCTYPE html>
<head>
<link id="theme_style" rel="stylesheet" type="text/css" href="" />
<link rel="stylesheet" type="text/css" href="main.css" />
<script id="srcTheme" src="toggleTheme.js"></script>
<style>
</style>
</head>
<body>
  <div id="text">テキスト</div>
  <div id="box1">ボックス1</div>
  <div id="box2">ボックス2</div>
  <br>
  <div id="toggleTheme">
    <svg role="img"  width=16 height=16 xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
      <path d="M8 256c0 136.966 111.033 248 248 248s248-111.034 248-248S392.966 8 256 8 8 119.033 8 256zm248 184V72c101.705 0 184 82.311 184 184 0 101.705-82.311 184-184 184z"></path>
    </svg>
  </div>
</body>
</html>

CSSを作成

次にCSSを作成します。今回の例では3つのファイルを作成します。
main.css - レイアウト定義
light.css - ライトモード
dark.css - ダークモード

main.css
#box1,
#box2 {
  width: 100px;
  height: 100px;
  margin: 10px;
}

#box2 {
  border: solid 1px;
}

#toggleTheme:hover {
  cursor: pointer;
}
light.css
body {
  color: #000;
  background-color: #fff;
}

#box1 {
  color: #fff;
  background-color: #000;
}

#box2 {
  border-color: #fff;
}

#toggleTheme svg {
  fill: #666;
}
dark.css
body {
  color: #e0e0e0;
  background-color: #222;
}

#box1 {
  color: #222;
  background-color: #e0e0e0;
}

#box2 {
  border-color: #222;
}

#toggleTheme svg {
  fill: #bbb;
}

テーマ切り替え用スクリプト

テーマの切り替えはJavaScriptで行います。

以下のようにJSでlinkタグのhrefを指定すると、そのCSSが読み込まれます。

document.getElementById('theme_style').href = 'dark.css';

次にこのhrefをテーマの状態によって切り替える処理を追加したいのですがその前に、 テーマを切り替えた後、再度同じサイトに訪れた場合にも同じテーマが適用されて欲しいですね。 何らかの方法で指定したテーマを保存したいです。 データの保存はcookiesessionなどの方法がありますが、今回はlocalStrageを利用することにしました。

localStrageはセキュアではなく、文字列しか保存できないので、大事なデータを扱うのには向いていません。 しかし、今回の例ではサーバを通して整合性をチェックする必要が無いですし、JSで扱いやすいので採用しました。 また、一度保存すると基本的に永続的に保存されるという点も大きいです。逆にそれがNGなパターンもあるかと思いますが。

localStrageへのデータの保存は以下のように指定します。

localStorage.setItem('isDark', '1');

これでテーマ切り替えの素材は揃いましたのでロジックを組んでいきます。

localStorageisDarkがあればdark.cssを読み込み、 無ければlight.cssを読み込みます。 そして、特定の要素をtoggleスイッチとして用意し、そのスイッチを押すとlocalStorageisDarkを保存/削除するようにします。

具体的には以下のような処理になります。

toggleTheme.js
setThemeFile = function() {
  if (localStorage.getItem('isDark') === '1') {
    document.getElementById('theme_style').href = 'dark.css';
  } else {
    document.getElementById('theme_style').href = 'light.css';
  }
}

toggleDLMode = function() {
  if (localStorage.getItem('isDark') === '1') {
    localStorage.removeItem('isDark');
  } else {
    localStorage.setItem('isDark', '1');
  }
  setThemeFile();
}

setThemeFile();
window.addEventListener('load', function() {
  document.getElementById('toggleTheme').addEventListener('click', toggleDLMode);
});

ここでのポイントはsetThemeFile()addEventListenerloadの外に置いてある点です。 setThemeFile()を中に入れてしまうと、全ての要素が読み込まれた後にCSSファイルが読み込まれます。 環境によってはすべてのDOMが読み込まれた後にCSSが指定されることになり、一瞬チラついてしまうことがあります。 それを極力減らすためにsetThemeFile()を外に出して、DOMが読み込まれる前にCSSを指定しています。

これでダークモード対応とモードを切り替える処理を実装することができました。


Webサイトへのダークモードの実装は徐々に増えてきています。 サイト内で利用している色が多いとその分対応は難しいと思いますが、 ユーザーのことを考えれば早めに対応した方が良いのかもしれません。
ちなみにこのブログではしばらく対応しない予定です。

今回はprefers-color-schemeを使わない例を紹介しましたが、 サイトの特性に合わせて実装方法を検討すれば良いかと思います。