ライト / ダークモード切り替えボタンを付けてみた

2024/10/157
アイキャッチ
CONTENTS

ナビバーに追加した2つのボタン

見た目で大体予想は付くだろうとは思いますがを押すと明るい配色のライトモードに切り替わり、を押すとデフォルトのダークモードに戻るという機能のボタンです。

実装に当たり参考にしたのはこちらのサイト。調べたら他にも様々なスクリプトや実装方法がありましたが、こちらで紹介されてるコードが一番シンプルでわかりやすかったです。

ダークモードに任意で切り替えられるボタンをCSSとJavascriptで作ろう! | STROBOLIGHTS.tokyo (Wayback Machine)

導入のきっかけ

個人的に、白画面が眩しく感じるようになってきたのとデザイン的にも好みだったのでずっとダークテーマにしていたものの、暗い画面よりも明るい画面の方が好み & 見やすいという方もいるだろうし、ライトモードという選択肢も用意した方が親切だろうなと思いつつ、面倒くさそうなのでずっと保留にしていました。

最近たまたま、Blogger 独自の CSS テーマデザイナーの代わりになる カスタムプロパティというのを知ってテスト導入してたんですが、カスタムプロパティを使えば個別ページや HTML / JavaScript ウィジェット内の <style> にも変更内容が自動的に適用できるし、ライトモード用の CSS の設定もしやすくなるということがわかり、2種類の CSS を用意してみる気になったというわけなんです。

ちなみに当ブログのライトモード用の CSS はこちら。元々のデザインがシンプルなせいもありますが、たったこれだけで済んじゃうんですw
この20数行を上書きするとライトモードに変わります。カスタムプロパティってほんと便利ですね(^^;

:root {
--bg1: #e5e5ff;
--bg2: #fbfbff;
--bg3: #f0f0ff;
--bg4: #e0e0ff;
--bg1-t: rgba(229, 229, 255, .8);
--shadow: #bbb;
--brand: dodgerblue;
--subbrand: coral;
--color: #333;
--darkcolor: #666;
--lightcolor: #fff;
--negacolor: #fff;
--link: dodgerblue;
}
#header {
background-color: var(--brand);
}
#header-text {
color: var(--lightcolor);
}
.search-box-form {
background: var(--bg2);
}
.mrp-post {
background: var(--bg2)!important;
}

ライトモードに切り替わるタイミングに関して、当初は prefers-color-scheme のメディアクエリでOS側の設定に合わせてダーク/ライトが自動で切り替わる仕様でいくつもりだったけど、訪問者の好みで自由に切り替えられるようにしたらもっと便利になるんじゃないかなと思い、ボタンによる切替式にしました。

機能について

まだ機能的に不十分で色々突っ込みどころもあるかもしれませんが、その辺はご容赦くださいw
例えば、ページ遷移やリロードをするとデフォルトのダークモードに戻ってしまうことや、ライトモードのボタンを押してから明るい画面に切り替わるまで若干のタイムラグがあることなど…。

間を置いて明るくなるのは、ライトモード用の CSS ファイルを外部(Googleドライブ)に置いてるため読み込みが遅くなってしまうせいなんですが、この挙動ってなんとなく蛍光灯を点けた時の感じに似ててこれはこれで趣があっていいな思うんだけどどうでしょう?w

選択状態を保持させるのは JavaScript でクッキーを制御(ブラウザに記憶)させればできるらしいけど、調べてもいまいち理解できなかったので諦めましたw
ボタンもナビバーと一緒に着いてくるので、どの位置にいてもいつでも切り替えられるということで不便はないと思いますけど。

もし要望があれば改善も検討はしますが、私は現状で十分満足してますのであまり期待はしないでください(^^;

カスタマイズの詳細は

当ブログ独自のカスタマイズ箇所との兼ね合いもあり、あまり参考にならないと思いますので詳細は割愛します。興味のある方はご自身で調べてトライしてみてくださいね。
無知な私でも出来たのでさほど難易度は高くないはずw

2021/04/10 追記: 別のスクリプトに変更しました

ライトモード導入方法に関して調べ始めた時、最初にこちらの記事を見つけて良さそうだなと思ったものの、ちょっと素人にはハードル高そうな気がして手も付けずに一旦パスしてたのですが、ダメ元でやってみたところ上手く出来そうだったのでこちらに乗り換えることにしました。

Qiita(@whike_chan さん)で紹介されているコードをベースにして、SANOGRAPHIX さんのカスタマイズ方法を参考に、カスタムプロパティで CSS を切り替えるようにしています。

ファビコンSANOGRAPHIX Blog
サムネイル
このブログをダークモードに対応する - SANOGRAPHIX Blog
なんとかは黒に染まれ、とかなんとか すでにお気づきの方もいらっしゃるかもしれないが、このブログにダークモードを追加した。
https://text.sanographix.net/entry/enable-dark-mode
ファビコンQiita
サムネイル
サイトにダークモード切り替えボタンを置こう - Qiita
https://qiita.com/whike_chan/items/3fff6d0c78fa74253d4d

当初のスクリプトとの仕様の違い

  1. prefers-color-scheme により初回訪問時は閲覧者のデバイスのモードに合わせてダーク/ライトが自動で切り替わる。
  2. 選択したモードはページ遷移やリロードしても保持される。(クッキー有効時)
  3. ライトモードの CSS も HTML 内にあるためボタンを押すと瞬時に切り替わる。
  4. ボタンを押すたびにが回転して入れ替わる。(→回転を無効化させました)

1~3は理想通りの仕様にできて満足なんですが、4のボタンが回転するというちょっと癖のある挙動がなんか微妙な気もしなくはないですw
SANOGRAPHIX さんみたいなシンプルに切り替わるボタンにできたら最高なんですけど、スクリプトを弄って何とかするのは私にはまだ無理そうです(^^;
というわけで、回転させるための transition:transform を削除するという単純な方法で回転を無効化させましたw

当ブログ独自カスタマイズのコードとデモ (2022/08/21: HTML と CSS を改良しました)

参考までにコードを載せておきます。
環境によって設置位置等も異なり、微調整やカスタムプロパティの設定も必要になりますので各自で対応してください。

JS はほぼそのままですが、HTML と CSS はだいぶ改造しています。

See the Pen Mode Switch Demo by Fujiyan (@fujiyanx) on CodePen.

2021/06/08 追記: 埋め込みツイートのテーマの明暗も切り替え可能に

スクリプトを弄って Twitter の埋め込みコンテンツのテーマの明暗も切り替わるようにしてみました。
但し…ボタンを押しただけでは駄目で、その後リロードしないと反映されません(^^;
埋め込みツイートのテーマは、ローカルの CSS ではなく、Twitter の外部 JS からデータを読み込んでいるため、どうしてもこういう仕様になってしまうようです。
明暗スクリプトをさらに弄れば即反映できるようになるのかな?…いずれにしても自力では無理そうなのでやりませんけどねw

改造箇所について

真似される方がいるかどうかは分かりませんが、一応参考までに詳細を書いておきます。

メタタグ

まず、<head>~</head> 内に Twitter のテーマ用のメタタグを設置します。

ちなみに元はこういうタグで、以下のように変更しました。(id は別に何でも構いません)

<meta content='light' name='twitter:widgets:theme'/>
↓↓

<meta content='light' id='twth' name='twitter:widgets:theme'/>

JavaScript

赤文字の部分を追記しました。

const checkToggle = document.getElementById('js_mode_toggle');
const rotateIcon = document.getElementById('js_rotate');
const isLight = window.matchMedia('(prefers-color-scheme:light)').matches;
const keyLocalStorage = 'modekey';
const localTheme = localStorage.getItem(keyLocalStorage);
const twTheme = document.getElementById('twth');
let nowRotate = 0;
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');
}
});
function changeMode(mode) {
if (mode === 'light') {
document.documentElement.setAttribute('data-theme-mode', 'light');
twTheme.setAttribute('content', 'light');
checkToggle.checked = true;
} else if (mode === 'dark') {
document.documentElement.setAttribute('data-theme-mode', 'dark');
twTheme.setAttribute('content', 'dark');
checkToggle.checked = false;
}
}
function rotateInfinite() {
nowRotate += 180;
rotateIcon.style.transform = 'rotate(' + nowRotate + 'deg)';
}

ボタンで明暗を切り替える毎に、id で指定したメタタグ内に content='light' または content='dark' の属性が挿入される仕組みになっています。

2024/07/17 追記

ChatGPT さんにコードを整理してもらいました。

F-lightユーザーでこちらのコードに変えたい方は、テーマ編集にて <!-- モードスイッチの下の方にある
<script>/*<![CDATA[*//*]]>*/</script>の所のコードを下記のものと入れ替えてください。

const checkToggle = document.getElementById('js_mode_toggle');
const rotateIcon = document.getElementById('js_rotate');
const twTheme = document.getElementById('twth');
const keyLocalStorage = 'modekey';
const localTheme = localStorage.getItem(keyLocalStorage);
const isLight = window.matchMedia('(prefers-color-scheme: light)').matches;
let nowRotate = 0;
const applyTheme = (mode) => {
document.documentElement.setAttribute('data-theme-mode', mode);
twTheme.setAttribute('content', mode);
checkToggle.checked = (mode === 'light');
};
const rotateInfinite = () => {
nowRotate += 180;
rotateIcon.style.transform = `rotate(${nowRotate}deg)`;
};
if (localTheme === 'light' || (localTheme === null && isLight)) {
rotateInfinite();
applyTheme('light');
} else {
applyTheme('dark');
}
checkToggle.addEventListener('change', (e) => {
rotateInfinite();
const mode = e.target.checked ? 'light' : 'dark';
applyTheme(mode);
localStorage.setItem(keyLocalStorage, mode);
});

圧縮版

const checkToggle=document.getElementById("js_mode_toggle"),rotateIcon=document.getElementById("js_rotate"),twTheme=document.getElementById("twth"),keyLocalStorage="modekey",localTheme=localStorage.getItem(keyLocalStorage),isLight=window.matchMedia("(prefers-color-scheme: light)").matches;let nowRotate=0;const applyTheme=e=>{document.documentElement.setAttribute("data-theme-mode",e),twTheme.setAttribute("content",e),checkToggle.checked="light"===e},rotateInfinite=()=>{nowRotate+=180,rotateIcon.style.transform=`rotate(${nowRotate}deg)`};"light"===localTheme||null===localTheme&&isLight?(rotateInfinite(),applyTheme("light")):applyTheme("dark"),checkToggle.addEventListener("change",e=>{rotateInfinite();let t=e.target.checked?"light":"dark";applyTheme(t),localStorage.setItem(keyLocalStorage,t)});

匿名さん、これなら文句ないでしょw

2024/10/14 追記: 別仕様のコードの紹介

むーなかさんにコメントにて別仕様のコードをご提供頂きました。

こちらのコードはで最後に選択していたモードを記憶するのではなく、PCやスマホのOSのモードに合わせて明暗が切り替わるようになります。(もちろん閲覧中にで別モードに変えることも可能です)

F-lightユーザーでこちらのバージョンに変えたい方は、テーマ編集にて <!-- モードスイッチの下の方にある
<script>/*<![CDATA[*//*]]>*/</script>の所のコードを下記のものと入れ替えてください。

const checkToggle = document.getElementById('js_mode_toggle');
const isLight = window.matchMedia('(prefers-color-scheme: light)').matches;
function changeMode(mode) {
if (mode === 'light') {
document.documentElement.setAttribute('data-theme-mode', 'light');
checkToggle.checked = true;
} else if (mode === 'dark') {
document.documentElement.setAttribute('data-theme-mode', 'dark')
checkToggle.checked = false;
}
}
if (isLight) {
changeMode('light');
} else {
changeMode('dark');
}
checkToggle.addEventListener('change', function (e) {
if (e.target.checked) {
changeMode('light');
} else {
changeMode('dark');
}
});

圧縮版

const checkToggle=document.getElementById("js_mode_toggle"),isLight=window.matchMedia("(prefers-color-scheme: light)").matches;function changeMode(e){"light"===e?(document.documentElement.setAttribute("data-theme-mode","light"),checkToggle.checked=!0):"dark"===e&&(document.documentElement.setAttribute("data-theme-mode","dark"),checkToggle.checked=!1)}isLight?changeMode("light"):changeMode("dark"),checkToggle.addEventListener("change",function(e){e.target.checked?changeMode("light"):changeMode("dark")});