diff --git a/assets/js/restore-theme.js b/assets/js/restore-theme.js index fb8c66ca..57903007 100644 --- a/assets/js/restore-theme.js +++ b/assets/js/restore-theme.js @@ -1,17 +1,13 @@ -/* eslint-disable no-var */ - // A super tiny script to restore dark mode off the bat (to hopefully avoid blinding flashes of white). // NOTE: This is inlined by Hugo/esbuild (see layouts/partials/head/restore-theme.html) instead of Webpack. +// ANOTHER NOTE: Whenever this code is changed, its (minified) CSP hash *MUST* be updated manually in vercel.json. -import { getDarkPref } from "./src/utils/theme.js"; +import { getDarkPref, updateDOM } from "./src/utils/theme.js"; try { - var cl = document.documentElement.classList; - var pref = getDarkPref(); - - // set `` if either the user has explicitly toggled in the past or if their OS is in dark mode - if (pref === "true" || (!pref && window.matchMedia("(prefers-color-scheme: dark)").matches)) { - cl.remove("light"); - cl.add("dark"); - } -} catch (error) {} + // Set root class and color-scheme property to dark if either... + // - the user has explicitly toggled it previously. + // - the user's OS is in dark mode. + const pref = getDarkPref(); + updateDOM(pref === "true" || (!pref && window.matchMedia("(prefers-color-scheme: dark)").matches)); +} catch (e) {} diff --git a/assets/js/src/components/ThemeToggle.js b/assets/js/src/components/ThemeToggle.js index 5911136d..24b60adb 100644 --- a/assets/js/src/components/ThemeToggle.js +++ b/assets/js/src/components/ThemeToggle.js @@ -1,6 +1,6 @@ import { h } from "preact"; import { useState, useEffect, useCallback } from "preact/hooks"; -import { isDark, getDarkPref, setDarkPref } from "../utils/theme.js"; +import { isDark, getDarkPref, setDarkPref, updateDOM } from "../utils/theme.js"; // react components: import BulbOn from "../assets/bulb-on.svg"; @@ -28,11 +28,8 @@ const ThemeToggle = () => { } catch (e) {} }, [saved, matchCallback]); - useEffect(() => { - // sets appropriate `` - document.documentElement.classList.remove("dark", "light"); - document.documentElement.classList.add(dark ? "dark" : "light"); - }, [dark]); + // sets appropriate `` and `color-scheme` CSS property when mode changes + useEffect(() => updateDOM(dark), [dark]); const handleToggle = () => { // only update the local storage preference if the user explicitly presses the lightbulb diff --git a/assets/js/src/utils/theme.js b/assets/js/src/utils/theme.js index f05b9081..7a7976fb 100644 --- a/assets/js/src/utils/theme.js +++ b/assets/js/src/utils/theme.js @@ -1,3 +1,7 @@ +// class names (``) for each theme +const lightClass = "light"; +const darkClass = "dark"; + // store preference in local storage const storageKey = "dark_mode"; export const getDarkPref = () => localStorage.getItem(storageKey); @@ -5,4 +9,17 @@ export const setDarkPref = (pref) => localStorage.setItem(storageKey, pref); // use the `` as a hint to what the theme was set to outside of the button component // there's probably (definitely) a cleaner way to do this..? -export const isDark = () => document.documentElement.classList.contains("dark"); +export const isDark = () => document.documentElement.classList.contains(darkClass); + +// sets appropriate `` and `color-scheme` CSS property +export const updateDOM = (dark) => { + const root = document.documentElement; + + // set `` + root.classList.remove(darkClass, lightClass); + root.classList.add(dark ? darkClass : lightClass); + + // ...and the CSS `color-scheme` property: + // https://developer.mozilla.org/en-US/docs/Web/CSS/color-scheme + root.style.colorScheme = dark ? "dark" : "light"; +}; diff --git a/vercel.json b/vercel.json index d119974b..e19bd101 100755 --- a/vercel.json +++ b/vercel.json @@ -78,7 +78,7 @@ }, { "key": "Content-Security-Policy", - "value": "default-src 'self'; base-uri 'none'; connect-src 'self' api.github.com hcaptcha.com *.hcaptcha.com platform.twitter.com; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self' jakejarvis.github.io buttons.github.io codepen.io hcaptcha.com *.hcaptcha.com platform.twitter.com www.youtube-nocookie.com; img-src 'self' data: https:; manifest-src 'self'; media-src 'self' data: https:; object-src 'none'; prefetch-src 'self'; script-src 'self' buttons.github.io gist.github.com hcaptcha.com *.hcaptcha.com syndication.twitter.com platform.twitter.com 'sha256-Y3FGTtMcy51rAwUUANOMGIiD/pCyH632fQcs70zcyrs='; style-src 'self' 'unsafe-inline' github.githubassets.com; worker-src 'self'; block-all-mixed-content; report-uri https://jarv.is/api/report/?csp; report-to default" + "value": "default-src 'self'; base-uri 'none'; connect-src 'self' api.github.com hcaptcha.com *.hcaptcha.com platform.twitter.com; font-src 'self'; form-action 'self'; frame-ancestors 'self'; frame-src 'self' jakejarvis.github.io buttons.github.io codepen.io hcaptcha.com *.hcaptcha.com platform.twitter.com www.youtube-nocookie.com; img-src 'self' data: https:; manifest-src 'self'; media-src 'self' data: https:; object-src 'none'; prefetch-src 'self'; script-src 'self' buttons.github.io gist.github.com hcaptcha.com *.hcaptcha.com syndication.twitter.com platform.twitter.com 'sha256-Z7KKYp58btYAbY6gBkS+wlIF+BEGe0Q3NYj5FiKGT8M='; style-src 'self' 'unsafe-inline' github.githubassets.com; worker-src 'self'; block-all-mixed-content; report-uri https://jarv.is/api/report/?csp; report-to default" }, { "key": "Report-To",