started work on post
This commit is contained in:
parent
c3d37f1d4e
commit
d371520bb7
6 changed files with 85 additions and 154 deletions
18
posts/platonically-ideal-light-dark-toggle.md
Normal file
18
posts/platonically-ideal-light-dark-toggle.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
title: "Making a platonically ideal light/dark theme toggle on the web"
|
||||||
|
|
||||||
|
description: ""
|
||||||
|
|
||||||
|
date: "2025-08-20"
|
||||||
|
|
||||||
|
draft: true
|
||||||
|
|
||||||
|
tags: []
|
||||||
|
---
|
||||||
|
|
||||||
|
Many blogs have a button you can use to toggle between their light and dark themes.
|
||||||
|
Unfortunately this button is often done in the obvious way which has issues.
|
||||||
|
|
||||||
|
## The Obvious Way
|
||||||
|
|
||||||
|
[demo]()
|
67
static/demos/light-dark-demo-1/index.html
Normal file
67
static/demos/light-dark-demo-1/index.html
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>Demo of the simplest implementation of a light/dark theme toggle</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<style>
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
body {
|
||||||
|
color: black;
|
||||||
|
background: white;
|
||||||
|
--button-bg: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.toggled {
|
||||||
|
color: white;
|
||||||
|
background: black;
|
||||||
|
--button-bg: #888;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
body {
|
||||||
|
color: white;
|
||||||
|
background: black;
|
||||||
|
--button-bg: #888;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.toggled {
|
||||||
|
color: black;
|
||||||
|
background: white;
|
||||||
|
--button-bg: #888;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: inherit;
|
||||||
|
background: var(--button-bg);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script async>
|
||||||
|
function toggleTheme() {
|
||||||
|
if (!document.body.classList.contains("toggled")) {
|
||||||
|
document.body.classList.add("toggled")
|
||||||
|
localStorage.setItem("toggled-theme", "yes");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.body.classList.remove("toggled")
|
||||||
|
localStorage.setItem("toggled-theme", "no");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (localStorage.getItem("toggled-theme") === "yes") {
|
||||||
|
document.body.classList.add("toggled");
|
||||||
|
}
|
||||||
|
console.log(localStorage.getItem("toggled-theme") === "yes");
|
||||||
|
</script>
|
||||||
|
<p>This is a basic demo, it needs Javascript to make the button work</p>
|
||||||
|
<button onclick="toggleTheme()">Toggle the theme</button>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -1,40 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<link rel="stylesheet" href="style.css" type="text/css">
|
|
||||||
<script type="module" src="script.js"></script>
|
|
||||||
<meta http-equiv="content-security-policy" content="worker-src 'self';">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<!--
|
|
||||||
in the context of github pages where we have no
|
|
||||||
backend the form element is for demonstration
|
|
||||||
rather than actual function in the demo
|
|
||||||
in an implementation with a proper backend the
|
|
||||||
form element would allow for the toggle button
|
|
||||||
to work without Javascript
|
|
||||||
-->
|
|
||||||
<form action="" method="post">
|
|
||||||
<!--
|
|
||||||
hidden because if a user agent doesn't support
|
|
||||||
CSS we don't want to show the light-dark toggle
|
|
||||||
|
|
||||||
id present so the css can make it visible again and js can grab it
|
|
||||||
-->
|
|
||||||
<input id="light_dark_toggle" type="submit" value="Toggle light/dark theme" hidden>
|
|
||||||
</form>
|
|
||||||
<p class="hide">Light and/or dark theming is only applicable if you have CSS, if you see this then either the CSS
|
|
||||||
hasn't loaded yet or your user agent doesn't support CSS</p>
|
|
||||||
<noscript>
|
|
||||||
<p>While an implementation with a proper backened wouldn't need javascript this demo does due to being hosted on
|
|
||||||
github pages. Specifically the javascript is needed to create a service worker to do the work a backend
|
|
||||||
would be doing otherwise.</p>
|
|
||||||
</noscript>
|
|
||||||
<p>Hello this is some text, should be black on white background if you're light theme and white on black background
|
|
||||||
if you're dark theme</p>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,39 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const toggle_button = document.body.querySelector("#light_dark_toggle");
|
|
||||||
const root = document.body.parentElement;
|
|
||||||
|
|
||||||
toggle_button.addEventListener("click", (event) => {
|
|
||||||
|
|
||||||
// js will handle the needed logic so no need for a HTTP form submission
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
|
|
||||||
// !== could be used but the check is to see if the attribute is "true"
|
|
||||||
// this writing is intend to make that clearer
|
|
||||||
const theme_toggled = !(root.getAttribute("toggletheme")?.at(0) === "t");
|
|
||||||
|
|
||||||
// set the cookie so backend/service worker can do it's thing
|
|
||||||
// cookies have other options you may wanna set especially
|
|
||||||
// if you're using them for anything else but for this
|
|
||||||
// use case I see no reason to care
|
|
||||||
document.cookie = `theme_toggled=${theme_toggled}`;
|
|
||||||
|
|
||||||
|
|
||||||
root.setAttribute("toggletheme", theme_toggled);
|
|
||||||
});
|
|
||||||
|
|
||||||
// If you wanna implement this for a site with a backend
|
|
||||||
// then feel free to ignore everything below I need it because
|
|
||||||
// I'm using github pages to host this rather than a proper
|
|
||||||
// backend
|
|
||||||
if (!("serviceWorker" in navigator)) {
|
|
||||||
alert("Your browser doesn't support service workers so this demo won't work properly.");
|
|
||||||
|
|
||||||
}
|
|
||||||
else setupServiceWorker();
|
|
||||||
async function setupServiceWorker() {
|
|
||||||
await navigator.serviceWorker.register("sw.js", {
|
|
||||||
scope: "/static/demos/light-dark-superior_v1/"
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
.hide {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark){
|
|
||||||
:root {
|
|
||||||
--background-color: black;
|
|
||||||
--text-color: white;
|
|
||||||
}
|
|
||||||
:root[toggletheme="true"]{
|
|
||||||
--background-color: white;
|
|
||||||
--text-color: black;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media (prefers-color-scheme: light){
|
|
||||||
:root {
|
|
||||||
--background-color: white;
|
|
||||||
--text-color: black;
|
|
||||||
}
|
|
||||||
:root[toggletheme="true"]{
|
|
||||||
--background-color: black;
|
|
||||||
--text-color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
background: var(--background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#light_dark_toggle{
|
|
||||||
display:block;
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// @returns Response
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Response
|
|
||||||
async function fetchResponse(event) {
|
|
||||||
//https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/request
|
|
||||||
//https://developer.mozilla.org/en-US/docs/Web/API/Request
|
|
||||||
const { request } = event;
|
|
||||||
const resp = await fetch(request);
|
|
||||||
const body = await resp.text();
|
|
||||||
console.log(request.headers);
|
|
||||||
const cookies = request.headers.getSetCookie();
|
|
||||||
console.log(cookies);
|
|
||||||
let theme_toggled = false;
|
|
||||||
for (let cookie of cookies) {
|
|
||||||
const [key, value] = cookie.split("=");
|
|
||||||
if (key != "theme_toggled") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
theme_toggled = value[0] === "t";
|
|
||||||
}
|
|
||||||
// this is a somewhat brittle way of accomplishing our desired behavior
|
|
||||||
const new_body = body.replace(`<html lang="en">`,
|
|
||||||
`<html lang="en" toggletheme="${theme_toggled}">`);
|
|
||||||
console.log(new_body);
|
|
||||||
return new Response(new_body, resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// needed if we actually want to get requests
|
|
||||||
//self.addEventListener('activate', (event) => event.waitUntil(self.clients.claim()));
|
|
||||||
|
|
||||||
self.addEventListener('fetch', (event) => {
|
|
||||||
console.log("Fetch:", event.request.url);
|
|
||||||
if (!event.request.url.endsWith(".html") && event.request.url.match("pagwin.xyz/.+\\..+") !== null) {
|
|
||||||
console.log("blocked");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith
|
|
||||||
event.respondWith(fetchResponse(event));
|
|
||||||
})
|
|
Loading…
Reference in a new issue