30
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

サイトにダークモード切り替えボタンを置こう

Last updated at Posted at 2020-01-09

こんな感じのをつくりました(絵文字をクリックしてね!)。

See the Pen dark mode support js by MAKI I (@IKEMAKI) on CodePen.

はじめに

ここのところダークモードがブイブイいわせてますね。アプリや記事メインのWebサイトはどんどこ対応していってる印象です。
LINEのダークモード表示がかっこよくて好きです。

自分はまだお仕事のWeb制作でダークモード対応をしたことはないのですが、これからありそうだよね~と話が出たり、個人的に眩しいのが苦手でダークモード好きだったりなどの理由でいじってみました。ヒアウィーゴ

ざっくり何してるか

  • 見てる人のパソコンやスマホがダークモードかライトモードか調べて、設定されてたらあわせる
  • 設定されてなかったら、デフォルトのダークモードで表示する(私がダークモード好きだから)
  • チェックボックスを押されたらモードを切り替えて、ローカルストレージにどっちを選んだか保存しておく(次回訪問時には選んだモードがデフォルトになる)
  • 太陽と月の絵文字を置いといて、切り替えるたびぐるぐる回す

コード

HTML

<!-- 表示を切り替えてみたい要素  -->
<p class="msg light">light mode</p>
<p class="msg dark">dark mode</p>

<!-- モード切替スイッチ -->
<div class="switch">
  <label class="switch-label">
    <input type="checkbox" id="js_mode_toggle">
    <span class="switch-mode" id="js_rotate"></span>
  </label>
</div>

ぶっちゃけHTMLはなんでもいいです。
今回は絵文字もCSSで表示しちゃってますし、モードを手動で切り替える機能がいらなければユーザー側のスイッチも不要なので、好きな感じにしちゃっておkです。

CSS

/* デフォルトはダークモード表示 */
body {
  margin: 0;
  padding: 1em 2em;
  color: #AAAE8D;
  background-color: #1D1D2C;
  transition-duration: 1s;
  transition-property: color, background-color;
}
.msg {
  font-size: 30px;
  font-weight: bold;
}
.dark {
  display: block;
}
.light {
  display: none;
}

/* ライトモードのとき */
.js-mode-light {
  color: #000;
  background-color: whitesmoke;
}
.js-mode-light .light {
  display: block;
}
.js-mode-light .dark {
  display: none;
}

/* モード切り替えボタン */
input[type="checkbox"] {
  display: none;
}
.switch {
  position: fixed;
  top: .5em;
  right: .5em;
  font-size: 60px;
}
.switch-label {
  cursor: pointer;
}
.switch-mode {
  position: absolute;
  top: -120px;
  right: -120px;
  display: block;
  width: 200px;
  height: 200px;
  transform: rotate(0);
  transition: transform 1s;
}
/* 月と太陽を対角線上に設置しておく。
   設定したspanを回したら太陽がひっくり返ってしまったので、transform scaleで上下反転した */
.switch-mode::before {
  content: "🌙";
  position: absolute;
  left: 0;
  bottom: 0;
}
.switch-mode::after {
  content: "🌄";
  position: absolute;
  right: 0;
  top: 0;
  transform: scale(1,-1);
}

今回はモードを判定するCSSのメディアクエリは使っていません。またあとで説明します。

表示の切り替えは、ライトモードを設定されたらjsでbodyにjs-mode-lightというクラスをつけるようにしました。ライトモード時にあてたいスタイルを.js-mode-lightに書いていけばおkです。
たとえばレスポンシブ対応で、スマホ表示のときはpcクラスを非表示にする……みたいなのと同じ感覚です。

ただ、モードを手動で切り替えられるスイッチを付けた場合、レスポンシブ対応のようにただ表示/非表示を切り替えてしまっては味気ないので、色などは適宜transitionをうまく使って設定したほうが良いです。

また変化が広範囲におよんで事故る可能性も高いので、transition-propertyでちゃんと適用したいプロパティを定義しておくのも大事です。日頃からとりあえず流れるようにtransition: all .2s;と打ってしまう私のようではだめということです。

色とか数値は全部テキトーです。

JS

// 要素やクラスを指定しておく
const checkToggle = document.getElementById('js_mode_toggle');
const rotateIcon = document.getElementById('js_rotate');
const classLight = 'js-mode-light';

// デバイスがライトモードかどうかチェック
const isLight = window.matchMedia('(prefers-color-scheme: light)').matches;

// ローカルストレージに保存するための適当なKey名
const keyLocalStorage = 'whike-theme-mode';

// ローカルストレージの情報を取得
const localTheme = localStorage.getItem(keyLocalStorage);

// 絵文字を回転させる角度
let nowRotate = 0;

// ローカルストレージの中身と、端末がライトモードかどうか(ie,edgeには無意味)をチェック
if(localTheme === 'light') {
  // ローカルストレージの情報が優先
  rotateInfinite();
  changeMode('light');
} else if(localTheme === 'dark') {
  changeMode('dark');
} else if(isLight) {
  rotateInfinite();
  changeMode('light');
}

// チェックボックスでの切り替え、選択をローカルストレージに保存
// モード切替スイッチが変更されたら発動
checkToggle.addEventListener('change', function(e) {
  // 絵文字大回転
  rotateInfinite();

  // チェックされたらライトモード、されなければダークモードにし、ローカルストレージにどちらを選んだか保存する
  if(e.target.checked) {
    changeMode('light');
    localStorage.setItem(keyLocalStorage,'light');
  } else {
    changeMode('dark');
    localStorage.setItem(keyLocalStorage,'dark');
  }
});

/**
 * テーマ切り替え
 * @param {String} mode 'light' もしくは 'dark'
 */
function changeMode(mode) {
  // 引数にしたがってbodyにクラスをつける
  // チェックボックス経由で変更かかったときはいいんだけど、ローカルストレージとかからモードを変えた場合にチェック状態がおかしくなるので、合わせておく
  if(mode === 'light') {
    document.body.classList.add(classLight);
    checkToggle.checked = true;
  } else if(mode === 'dark') {
    document.body.classList.remove(classLight);
    checkToggle.checked = false;
  }
}

/**
 * 月と太陽アイコン無限回転
 * 呼ばれるたびに180度角度が追加されていく
 */
function rotateInfinite() {
  nowRotate += 180;
  rotateIcon.style.transform = 'rotate(' + nowRotate + 'deg)';
}

デバイスのテーマを調べる

デバイスのテーマを調べるには、CSSで用意されているprefers-color-schemeを使います。

prefers-color-scheme は CSS のメディア特性で、ユーザーがシステムに要求したカラーテーマが明色か暗色かを検出するために使用します。
by prefers-color-scheme - CSS: カスケーディングスタイルシート | MDN

なのでこれを使えばCSSだけで切り替えることができます。
こんなかんじで👇

.dark { display: block; }
.light { display: none; }

@media (prefers-color-scheme: light) {
  .dark { display: none; }
  .light { display: block; }
}

ただIEとEdgeが非対応なので、そこも対応する場合は(するよね??)結局JSを使うことになるため、両方で管理したくないっという思いから今回はJSだけ使っています。

参考: Can I use prefers-color-scheme ?

で、それをJSで使うときはwindow.matchMediaを使います。
ためしに開発者ツールのコンソールにwindow.matchMedia('(prefers-color-scheme: light)').matchesをコピペしてEnterすると、ライトモードならtrue、ダークモードならfalseが返ります。私はPCをダークモードにしているのでfalseになりました。
これでユーザーのデバイステーマを判定できました。

ユーザーの選択したテーマを保存

テーマを選択するボタンを設置した以上は、次回サイト訪問時も選んだテーマになってて欲しいので、ユーザーがどちらを選んだかブラウザのローカルストレージに保存しておきます。

保存は簡単で、localStorage.setItem(任意のKey名, 値);とすればおkです。
今回は、ライトモードのときにはlocalStorage.setItem('whike-theme-mode','light');、ダークモードのときにはlocalStorage.setItem('whike-theme-mode','dark');としています。whikeは私の名前です。そうそう被らないとは思いますが、固有のKeyにするため付けてます。
setItemで情報を保存したら、getItemでlocalStorage.getItem(Key名);こういう感じに取り出せます。もし設定されてなければnullが返ります。
Chromeであれば、開発者ツールApplicationタブのStorage欄に、Local Storageというのがあるのでそこを展開してみると、設定したKeyとValueが確認できます。他のブラウザでもだいたい似たようなところにあります。IEでも大丈夫😉

ちなみにローカルストレージは気軽に扱えるぶん、個人情報とかログイン情報とか、大事なものは置いちゃダメですよ✊

おわりに

最初なんとなくで月と太陽が入れ替わったら楽しいデショと思って作ったのですが、回すのが意外と面倒😂この動きにしなかったらもう少しシンプルなコードにできた気がします。
もっと上手に回せそうな気がするんですが……😵

やってることは結構単純だし、このやり方であればIEやEdgeも動いたので、お仕事で使う分にも特に問題はなさそうかなーという印象でした。
たぶん大変なのはデザイナーさんの方ですね(¯―¯٥)

IEとEdgeはアレですが、cssのメディアクエリでやるやり方はすぐに試せると思うのでやってみてください😍
Win10であれば設定>個人用設定>色>色を選択する>白or黒切り替えでPCのテーマを変えられるので(Macも似たような感じでダーク/ライト切り替え)、それに合わせてWebページが変化するのがオモロイです~やっぱ見た目が変わるのは楽しい😍😍

コードに関しては、やりたいことを中心にモリモリ作ってしまったので、もっとスマートにやれそうだという箇所があればぜひ教えてください!

ではでは~

参考

30
15
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?