Razl-Dazl

eyecatch

ダークモード切替ボタンの実装

Posted at — 2023-02-04

一応CSSはダークモード対応にしていましたが、折角なのでダークモードの切替ボタンを設置しました


仕様

仕様は以下のようにしました

  1. 初回表示時のCSSはOSのテーマに合わせる
  2. 次回以降は、前回訪問時に手動でテーマを切り替えていた場合はそちらを優先する
    • OSがダークモードでも前回訪問時に手動でライトモードにしていた場合はライトモードにします、逆も同様
  3. 手動で切替えた際、現在のテーマを保存するが、保存する情報とOSのテーマが一致する場合は保存せずに削除する

3に関しては

  • OSのテーマはダークモードとする
  • 初回表示後、切替ボタンで一旦ライトモードにしてまたダークモードに戻す
  • 一回Webページを閉じてOS側をライトモードに設定してから再度Webページを開く 以上の場合の時にWebページはライトモードで表示すべきなので(個人的に)、このような仕様にする必要があります

ダークモードの実装方法はググればいくらでも出てくるのですが3を考慮したものとかそういう詳しいのは出て来なかったので結局自分で実装することになりました

ソースは最終的に以下のような感じになりました

const osTheme = window.matchMedia("(prefers-color-scheme: dark)")
osTheme.addEventListener("change", onOsThemeHasChanged)

const cache = localStorage.getItem("theme")
const osThemeIsDark = osTheme.matches

/* 仕様1と2 */
if (cache === "dark") {
    toDark()
} else if (cache === "light") {
    toLight()
} else if (osThemeIsDark) {
    toDark()
} else {
    toLight()
}  

/* 切替ボタン関連の処理は画面読み込み後に行う(nullってしまうの防止) */
window.onload = function(){
    const toggle = document.getElementById("darkmode-toggle")
    toggle.checked = currentThemeIsDark()
    toggle.addEventListener("click", onToggleHasChanged)
}

function onToggleHasChanged(event) {
    const isDark = event.target.checked
    if (isDark) {
        toDark()
    } else {
        toLight()
    }  
    syncLocalStorage(isDark)
}  

function onOsThemeHasChanged() {
    const osThemeIsDark = window.matchMedia("(prefers-color-scheme: dark)").matches

    if (osThemeIsDark) {
        toDark()
    } else {
        toLight()
    }
    syncLocalStorage(osThemeIsDark)
}

/* 仕様3 */
function syncLocalStorage(isDark) {
    const osThemeIsDark = window.matchMedia("(prefers-color-scheme: dark)").matches

    if (isDark == osThemeIsDark ){
        localStorage.removeItem("theme")
        return
    }  

    if (isDark) {
        localStorage.setItem("theme", "dark")
    } else {
        localStorage.setItem("theme", "light")
    }
}

function toDark() {
    document.documentElement.setAttribute("theme", "dark")
}

function toLight() {
    document.documentElement.setAttribute("theme", "light")
}

function currentThemeIsDark() {
    return (document.documentElement.getAttribute("theme") === "dark")
}

切替ボタン関連の処理はwindow.onloadを用いてタイミングを遅らせています htmlを参照して切替ボタンを取得する際にnullってしまうのを防止する為です

あまりコメントは入れてないですが読みやすいコードにはなったかなと思います

function多い気もしますが一応役割毎に分けたつもり・・・

以下CSS

:root {
    --background-color: #fff;
    --text-color: #333;
    --primary-color: #a00;
    --border-color: #ddd;
    --codeblock-color: #fafafa;
}

:root[theme = "dark"] {
    --background-color: #111;
    --text-color: #ddd;
    --primary-color: #ff7979;
    --border-color: #333;
    --codeblock-color: #333;
}

a {
    color: var(--primary-color);
    text-decoration: none;
    word-break: break-word;
}

...
    

ダークモード切替時、CSSの適用部分は色指定関連のみ差し替わるようにしました 色を指定する際はvar(--foo);みたいな感じで書いておけば参照先が自動で切り替わるので分かりやすいです

以下HTML

...
<div class="toggle">
  <input id="darkmode-toggle" type="checkbox" />
    <label class="darkmode-toggle-label" for="darkmode-toggle">
        <span id="darkmode-svg">
		    <!-- ここにアイコン用svg入れる-->
            ...        
        </span>
    </label>
</div>
...

ここは特段特別なことはしていません(ググってすぐ出てくるようなのと大体一緒) JavaScriptでは、切替ボタンの実態はid(darkmode-toggle)で取得しています


あまり特別なことはしていないのですが結果的に我流の実装になりました

私のググり方が下手なのかGoogleの精度が駄目になってきているのか・・・

Author@zakuro

Mastodon: 396@vivaldi.net