6.1 KiB
Instant Dark Mode Toggle — Plan
Problem
Currently, clicking the dark/light mode toggle button calls window.location.reload() on every page. This causes:
- Full page reload (network requests, DOM rebuild, script re-execution)
- Loss of scroll position, form state, and in-progress work
- A
sidenavWasOpenlocalStorage hack to restore sidenav state after reload - Jarring user experience compared to modern sites that toggle instantly
Root Cause Analysis
The CSS architecture is already well-designed for instant toggling:
- All colors flow through CSS custom properties defined in
:rootinclient.css - Dark mode overrides are applied via
html.dark-mode, body.dark-modeselectors - Most page styles correctly use
var(--primary-color),var(--secondary-color), etc.
The only reason the page reloads is that every theme toggle click handler ends with:
window.location.reload();
Instead of simply toggling the CSS class:
document.documentElement.classList.toggle('dark-mode');
document.body.classList.toggle('dark-mode');
What Needs to Change
1. Replace window.location.reload() with live CSS class toggle
In every HTML page's theme toggle click handler, replace the reload with:
document.documentElement.classList.toggle('dark-mode', isDarkMode);
document.body.classList.toggle('dark-mode', isDarkMode);
themeToggleHamburger.animateTo(isDarkMode ? 'moon' : 'circle');
Remove the sidenavWasOpen save/restore hack since it's no longer needed.
Affected active pages (~22 files in www/):
index.html,feed.html,post.html,note.html,msg.htmlai.html,ai-tv.html,html-tv.html,cashu.html,cal.htmlpeople.html,npub.html,notifications.html,event-management.htmlslide-show.html,conway.html,strudel.html,vj.htmlblobs.html,didactyl.html,block.html,skills-edit.htmlkeep-alive.html,todo.html,music.html
Affected old/template pages (~10 files in www/old/):
template.html,bunker.html,links.html,relays.htmlrelay-test.html,stream.html,stream-ctrl.htmlprofile.html,db.html,db_relay.html,event.htmltools.html,c-relay-pg.html,skills-demo.html
2. Fix hardcoded colors that won't respond to CSS variable changes
These need to be converted to use CSS variables so they update instantly:
CSS files:
messaging-ui.csslines 201, 208:background: #ffffff→background: var(--secondary-color)client.cssline 959:background: #000— check context, may be intentional for videostrudel.csslines 174-176, 451, 764-767, 841, 848: hardcoded error/delete colors — mostly accent colors, low priority
HTML inline <style> blocks (higher priority):
cal.html:background-color: #eee,#ddd,#f0f0f0,#ffe6e6— need dark-mode variantscashu.html: QR code backgrounds#ffffff— needvar(--secondary-color)html-tv.html/ai-tv.html: iframe backgrounds#ffffff— may be intentional for content displayindex.html:background-color: #ddd— needs variableslide-show.html:background: #111— likely intentional for slideshow
JS-generated inline styles:
index.htmlline 640-641: QR code palette and background use hardcoded hex — need to read current CSS variable values at render time- Error message templates across many pages:
color: #666— minor, only shown on errors
3. Add color-scheme CSS property for native UI elements
Add to client.css so scrollbars, form controls, and other browser-native elements respect the theme:
:root {
color-scheme: light;
}
html.dark-mode {
color-scheme: dark;
}
4. Add smooth transition for theme change
Add a brief CSS transition so the color swap feels polished:
html {
transition: background-color 0.2s ease, color 0.2s ease;
}
5. Special case: Strudel page
strudel.html calls syncStrudelTheme(isDarkMode) before reload. This should still be called, but without the reload — the Strudel editor's changeSetting API already handles live theme updates.
6. Remove sidenavWasOpen hack
After removing the reload, the save/restore of sidenavWasOpen in the theme toggle handler becomes dead code. Remove it from all pages. The restore logic at page init can stay for other use cases, or be removed if it's only used for theme toggle.
Architecture Diagram
flowchart TD
A[User clicks theme toggle] --> B[Toggle isDarkMode boolean]
B --> C[Save to localStorage]
C --> D[Toggle dark-mode class on html + body]
D --> E[CSS variables auto-update]
E --> F[All var references repaint instantly]
D --> G[Animate hamburger icon moon/circle]
D --> H{Special page?}
H -->|Strudel| I[Call syncStrudelTheme]
H -->|QR codes visible| J[Re-render QR with new palette]
H -->|Other| K[Done - no reload needed]
Implementation Order
client.css— Addcolor-schemeproperty and optional transition- Create shared
applyThemehelper — A small function that handles the class toggle, hamburger animation, and localStorage write. Could be added toutilities.mjsor kept inline. - Update all active HTML pages — Replace reload with live toggle
- Fix hardcoded colors in CSS files —
messaging-ui.css,client.css - Fix hardcoded colors in HTML inline styles —
cal.html,cashu.html,index.html - Handle Strudel special case — Keep
syncStrudelThemecall, remove reload - Remove
sidenavWasOpenhack from theme toggle handlers - Update
www/old/pages — Same pattern, lower priority - Test — Verify all pages toggle cleanly without visual artifacts
Risk Assessment
- Low risk: The CSS variable architecture is already solid. The main change is removing
window.location.reload()and adding class toggles. - Medium risk: Some pages may have JS that reads theme state at init time and caches colors. These would need a re-render callback. The QR code on
index.htmlis one known case. - Low risk: The
old/pages are lower traffic and can be updated in a follow-up pass.