# 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:
```js
window.location.reload();
```

Instead of simply toggling the CSS class:
```js
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:
```js
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: #ffffff` → `background: 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:

```css
: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:

```css
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

```mermaid
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 files** — `messaging-ui.css`, `client.css`
5. **Fix hardcoded colors in HTML inline styles** — `cal.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.
