Files
client/plans/instant-dark-mode-toggle.md
2026-04-17 16:52:51 -04:00

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 sidenavWasOpen localStorage 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 :root in client.css
  • Dark mode overrides are applied via html.dark-mode, body.dark-mode selectors
  • 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.html
  • ai.html, ai-tv.html, html-tv.html, cashu.html, cal.html
  • people.html, npub.html, notifications.html, event-management.html
  • slide-show.html, conway.html, strudel.html, vj.html
  • blobs.html, didactyl.html, block.html, skills-edit.html
  • keep-alive.html, todo.html, music.html

Affected old/template pages (~10 files in www/old/):

  • template.html, bunker.html, links.html, relays.html
  • relay-test.html, stream.html, stream-ctrl.html
  • profile.html, db.html, db_relay.html, event.html
  • tools.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.css lines 201, 208: background: #ffffffbackground: var(--secondary-color)
  • client.css line 959: background: #000 — check context, may be intentional for video
  • strudel.css lines 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 variants
  • cashu.html: QR code backgrounds #ffffff — need var(--secondary-color)
  • html-tv.html / ai-tv.html: iframe backgrounds #ffffff — may be intentional for content display
  • index.html: background-color: #ddd — needs variable
  • slide-show.html: background: #111 — likely intentional for slideshow

JS-generated inline styles:

  • index.html line 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

  1. client.css — Add color-scheme property and optional transition
  2. Create shared applyTheme helper — A small function that handles the class toggle, hamburger animation, and localStorage write. Could be added to utilities.mjs or kept inline.
  3. Update all active HTML pages — Replace reload with live toggle
  4. Fix hardcoded colors in CSS filesmessaging-ui.css, client.css
  5. Fix hardcoded colors in HTML inline stylescal.html, cashu.html, index.html
  6. Handle Strudel special case — Keep syncStrudelTheme call, remove reload
  7. Remove sidenavWasOpen hack from theme toggle handlers
  8. Update www/old/ pages — Same pattern, lower priority
  9. 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.html is one known case.
  • Low risk: The old/ pages are lower traffic and can be updated in a follow-up pass.