Fix light/dark mode preference not persisting on reload #17

Closed
opened 2026-04-14 10:01:41 +00:00 by casper-stevens · 2 comments
Member

Context

The theme toggle (light/dark mode) works during a session but resets to the default on every page reload, forcing users to re-apply their preference each time.

Goals

  • Persist the selected theme to localStorage on toggle
  • Read and apply the persisted theme on initial page load before first render to avoid a flash of the wrong theme
## Context The theme toggle (light/dark mode) works during a session but resets to the default on every page reload, forcing users to re-apply their preference each time. ## Goals - Persist the selected theme to localStorage on toggle - Read and apply the persisted theme on initial page load before first render to avoid a flash of the wrong theme
Author
Member

Implementation Spec for Issue #17

Objective

The theme toggle works in-session but resets on every reload because the selection is never written to storage and the page always boots with data-bs-theme="dark" hard-coded in the HTML. This spec describes the minimal, flash-free fix.

Requirements

  • On toggle, write the chosen theme ("light" or "dark") to localStorage under the key heroSlidesTheme
  • On page load, read heroSlidesTheme and apply it to <html data-bs-theme> before the browser paints (no flash)
  • The toggle button icon must reflect the restored theme on load
  • Fix is contained to two existing files only — no new files or dependencies

Files to Modify

File Change
crates/hero_slides_ui/templates/base.html Add inline <script> in <head> that reads localStorage and sets data-bs-theme immediately; remove hard-coded data-bs-theme="dark" from <html> tag
crates/hero_slides_ui/static/js/dashboard.js Add localStorage.setItem on toggle; add icon sync on initial load

Implementation Plan

Step 1: Remove hard-coded theme from <html> tag in base.html

  • Change <html lang="en" data-bs-theme="dark"> to <html lang="en">
  • The inline script (Step 2) becomes the single source of truth
    Dependencies: none

Step 2: Add blocking inline script to <head> in base.html

  • Insert as the first child of <head>, before any <link> or <meta>
  • Reads localStorage.getItem('heroSlidesTheme') and sets data-bs-theme; falls back to 'dark'
  • Self-invoking function to avoid global scope pollution
    Dependencies: Step 1

Step 3: Persist theme to localStorage on toggle in dashboard.js

  • Add localStorage.setItem('heroSlidesTheme', next) inside the existing toggle click handler
    Dependencies: none

Step 4: Sync toggle icon to restored theme on load in dashboard.js

  • Add a self-invoking block after the toggle handler that reads localStorage and sets the icon class
    Dependencies: none

Acceptance Criteria

  • Fresh load (no localStorage key) renders dark mode — same as today
  • After toggling to light and reloading, page shows light mode with no flash
  • After toggling back to dark and reloading, dark mode persists
  • Toggle icon always matches the active theme on load (sun = dark, moon = light)
  • Clearing localStorage and reloading falls back to dark mode without errors
  • No regressions to the iframe hero:theme postMessage bridge
## Implementation Spec for Issue #17 ### Objective The theme toggle works in-session but resets on every reload because the selection is never written to storage and the page always boots with `data-bs-theme="dark"` hard-coded in the HTML. This spec describes the minimal, flash-free fix. ### Requirements - On toggle, write the chosen theme (`"light"` or `"dark"`) to `localStorage` under the key `heroSlidesTheme` - On page load, read `heroSlidesTheme` and apply it to `<html data-bs-theme>` before the browser paints (no flash) - The toggle button icon must reflect the restored theme on load - Fix is contained to two existing files only — no new files or dependencies ### Files to Modify | File | Change | |---|---| | `crates/hero_slides_ui/templates/base.html` | Add inline `<script>` in `<head>` that reads localStorage and sets `data-bs-theme` immediately; remove hard-coded `data-bs-theme="dark"` from `<html>` tag | | `crates/hero_slides_ui/static/js/dashboard.js` | Add `localStorage.setItem` on toggle; add icon sync on initial load | ### Implementation Plan #### Step 1: Remove hard-coded theme from `<html>` tag in `base.html` - Change `<html lang="en" data-bs-theme="dark">` to `<html lang="en">` - The inline script (Step 2) becomes the single source of truth Dependencies: none #### Step 2: Add blocking inline script to `<head>` in `base.html` - Insert as the **first child of `<head>`**, before any `<link>` or `<meta>` - Reads `localStorage.getItem('heroSlidesTheme')` and sets `data-bs-theme`; falls back to `'dark'` - Self-invoking function to avoid global scope pollution Dependencies: Step 1 #### Step 3: Persist theme to localStorage on toggle in `dashboard.js` - Add `localStorage.setItem('heroSlidesTheme', next)` inside the existing toggle click handler Dependencies: none #### Step 4: Sync toggle icon to restored theme on load in `dashboard.js` - Add a self-invoking block after the toggle handler that reads localStorage and sets the icon class Dependencies: none ### Acceptance Criteria - [ ] Fresh load (no localStorage key) renders dark mode — same as today - [ ] After toggling to light and reloading, page shows light mode with no flash - [ ] After toggling back to dark and reloading, dark mode persists - [ ] Toggle icon always matches the active theme on load (sun = dark, moon = light) - [ ] Clearing localStorage and reloading falls back to dark mode without errors - [ ] No regressions to the iframe `hero:theme` postMessage bridge
Author
Member

Pull request opened: #19

Pull request opened: https://forge.ourworld.tf/lhumina_code/hero_slides/pulls/19
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_slides#17
No description provided.