PIANDT / PIANDT__backup /fixes /theme_toggle_fix.md
0001AMA's picture
Add PIANDT__backup folder (binaries excluded for HF)
90e6c1c
|
Raw
History Blame Contribute Delete
10.1 kB

Theme Toggle Fix - Complete Solution

Problem Description

The theme toggle button was not working correctly across all pages. The issues were:

  1. Only header responded to toggle: When clicking the theme toggle button, only the header/navbar changed color, but the body content (.content-text paragraphs) remained unchanged.

  2. Empty body class: When toggling, the body element's class attribute was being set to an empty string (class="") instead of properly toggling between class="dark-theme" and no class.

  3. Body remained dark when switching to light: When toggling from dark to light mode, the body background remained dark instead of switching to white.

  4. Inconsistent behavior: The theme toggle worked on index.html but not on sub-pages (in/in.html, processing/processing.html, out/out.html and their sub-pages).

Root Causes

1. CSS Variable Inheritance Issue

The .content-text p elements used color: var(--color-text-light), which relies on CSS variable inheritance. The CSS variables weren't being updated on all parent elements in the DOM hierarchy, causing the variables to resolve to dark values even when the theme was toggled.

2. Timing Issue with Stylesheet Injection

The dynamic stylesheet that overrides CSS rules was being injected too late - only when script.js loaded at the end of the body. By that time, the page had already rendered with incorrect colors.

3. ClassList.toggle() Issue

Using classList.toggle() was causing the class to be set to an empty string in some cases instead of properly adding/removing the dark-theme class.

4. Background Color Not Explicitly Reset

When switching from dark to light mode, the background colors weren't being explicitly set to white, causing them to remain dark.

Solution

1. Inline Stylesheet Injection in HTML Head

Location: All HTML files in in/, processing/, and out/ directories

Added stylesheet injection code directly in the inline <script> tag in the <head> section of each HTML file. This ensures the stylesheet is injected immediately when the page loads, before the body content renders.

// CRITICAL: Inject stylesheet immediately in head for content-text
const darkThemeStyle = document.createElement('style');
darkThemeStyle.id = 'dark-theme-override-style';
darkThemeStyle.textContent = ':root.dark-theme,html.dark-theme,body.dark-theme{--color-text:#ffffff!important;--color-text-light:#ffffff!important}body.dark-theme .content-text,html.dark-theme .content-text,body.dark-theme .content-text *,html.dark-theme .content-text *,body.dark-theme .content-text p,html.dark-theme .content-text p,body.dark-theme .section .content-text,html.dark-theme .section .content-text,body.dark-theme .section .content-text *,html.dark-theme .section .content-text *,body.dark-theme .section .content-text p,html.dark-theme .section .content-text p,body.dark-theme main .content-text,html.dark-theme main .content-text,body.dark-theme main .content-text *,html.dark-theme main .content-text *,body.dark-theme main .content-text p,html.dark-theme main .content-text p,body.dark-theme .container .content-text,html.dark-theme .container .content-text,body.dark-theme .container .content-text *,html.dark-theme .container .content-text *,body.dark-theme .container .content-text p,html.dark-theme .container .content-text p,body.dark-theme .content-grid .content-text,html.dark-theme .content-grid .content-text,body.dark-theme .content-grid .content-text *,html.dark-theme .content-grid .content-text *,body.dark-theme .content-grid .content-text p,html.dark-theme .content-grid .content-text p{color:#ffffff!important;--color-text:#ffffff!important;--color-text-light:#ffffff!important}body.dark-theme main,html.dark-theme main,body.dark-theme .section,html.dark-theme .section,body.dark-theme .container,html.dark-theme .container,body.dark-theme .content-grid,html.dark-theme .content-grid{background-color:#1a1a1a!important;color:#ffffff!important;--color-text:#ffffff!important;--color-text-light:#ffffff!important}body.dark-theme .content-grid h1,html.dark-theme .content-grid h1,body.dark-theme .content-text h1,html.dark-theme .content-text h1{color:#ffffff!important}';
if (document.head) {
    document.head.appendChild(darkThemeStyle);
} else {
    (function checkHead() {
        if (document.head) {
            document.head.appendChild(darkThemeStyle);
        } else {
            setTimeout(checkHead, 0);
        }
    })();
}

2. Explicit Class Management Instead of Toggle

Location: script.js - initThemeToggle() function

Changed from using classList.toggle() to explicitly checking the current state and adding/removing classes:

// Before (problematic):
body.classList.toggle('dark-theme');
html.classList.toggle('dark-theme');
document.documentElement.classList.toggle('dark-theme');

// After (fixed):
const currentlyDark = body.classList.contains('dark-theme');
if (currentlyDark) {
    // Switching to light
    body.classList.remove('dark-theme');
    html.classList.remove('dark-theme');
    document.documentElement.classList.remove('dark-theme');
} else {
    // Switching to dark
    body.classList.add('dark-theme');
    html.classList.add('dark-theme');
    document.documentElement.classList.add('dark-theme');
}

3. Explicit Background Color Reset

Location: script.js - removeInlineDarkThemeStyles() function

When switching to light mode, explicitly set background colors to white instead of just removing the dark background:

// CRITICAL: Remove dark background from html and body
document.documentElement.style.removeProperty('background-color');
document.documentElement.style.setProperty('background-color', '#ffffff', 'important');
document.body.style.removeProperty('background-color');
document.body.style.setProperty('background-color', '#ffffff', 'important');

// Also for main and section containers
if (main) {
    main.style.setProperty('background-color', '#ffffff', 'important');
    // ... remove other styles
}
if (section) {
    section.style.setProperty('background-color', '#ffffff', 'important');
    // ... remove other styles
}

4. Comprehensive Stylesheet with All Selectors

Location: script.js - applyInlineDarkThemeStyles() function

Created a comprehensive dynamic stylesheet that targets all possible selector combinations:

  • body.dark-theme .content-text p
  • html.dark-theme .content-text p
  • body.dark-theme .section .content-text p
  • body.dark-theme main .content-text p
  • body.dark-theme .container .content-text p
  • body.dark-theme .content-grid .content-text p
  • And all their wildcard variants (*)

This ensures that no matter the DOM structure, the styles are applied correctly.

5. Immediate Application (No setTimeout)

Location: script.js - Theme toggle click handler

Changed from using setTimeout to applying styles immediately:

// Before:
setTimeout(() => {
    if (isDark) {
        applyInlineDarkThemeStyles();
    } else {
        removeInlineDarkThemeStyles();
    }
}, 0);

// After:
if (isDark) {
    applyInlineDarkThemeStyles();
} else {
    removeInlineDarkThemeStyles();
}

Files Modified

  1. All HTML files (96 files) in:

    • in/ directory and subdirectories
    • processing/ directory and subdirectories
    • out/ directory and subdirectories

    Change: Added stylesheet injection code in the inline <script> tag in the <head> section.

  2. script.js:

    • Added IIFE at the top to inject stylesheet immediately on script load
    • Modified applyInlineDarkThemeStyles() function
    • Modified removeInlineDarkThemeStyles() function
    • Modified initThemeToggle() function to use explicit add/remove instead of toggle
    • Modified click handler to apply styles immediately
  3. styles.css:

    • Removed transitions for theme switching (set to 0.05s, then removed entirely)
    • Added comprehensive dark theme rules with !important flags

Testing

After implementing the fix, test on:

  • http://localhost:8000/index.html
  • http://localhost:8000/in/in.html
  • http://localhost:8000/processing/processing.html
  • http://localhost:8000/out/out.html
  • All sub-pages in these directories

Expected behavior:

  • Theme toggle should work instantly on all pages
  • Body class should be class="dark-theme" when dark, empty when light
  • Body background should be white (#ffffff) in light mode, dark (#1a1a1a) in dark mode
  • All text content (.content-text and children) should change color correctly
  • No visible transitions or delays

Key Takeaways

  1. Timing is critical: Stylesheets must be injected as early as possible (in the head, not at the end of body).

  2. CSS Variable inheritance: When using CSS variables, ensure they're set on all parent elements in the hierarchy.

  3. Explicit over implicit: Use explicit add()/remove() instead of toggle() when class state is critical.

  4. Comprehensive selectors: When dealing with complex DOM structures, target all possible selector combinations.

  5. Inline styles as fallback: Use inline styles with !important as a fallback when CSS specificity is an issue.

  6. Immediate application: Apply styles immediately rather than deferring with setTimeout when possible.

Version History

  • v=28: Final working version with all fixes applied
  • v=27: Added explicit background color reset
  • v=26: Added background color setting in applyInlineDarkThemeStyles
  • v=25: Fixed explicit class management
  • v=24: Changed from toggle to explicit add/remove
  • v=23: Added inline stylesheet injection in HTML head
  • v=22: Added IIFE for immediate stylesheet injection
  • v=21: Added comprehensive dynamic stylesheet
  • v=20: Added CSS variable updates on html/body
  • v=19: Extracted helper functions
  • v=18: Added inline style application
  • v=17: Added transition disabling
  • v=16: Initial theme toggle fixes

Date

Fixed: December 2024