Skip to content

Commit b58a8ea

Browse files
committed
@wip theme picker work
1 parent f1698ca commit b58a8ea

8 files changed

Lines changed: 348 additions & 303 deletions

File tree

THEME_SYSTEM_WIP.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Theme System - Work in Progress
2+
3+
## Current Status
4+
5+
### What's Working
6+
- ✅ Theme picker tests: 9/10 @ready (63 tests passing across 7 browsers)
7+
- ✅ Manual theme selection and persistence works correctly
8+
- ✅ Theme switching via theme picker UI works
9+
- ✅ View Transitions correctly maintain theme across page navigations
10+
- ✅ CSS architecture refactored: `@theme inline` uses `var()` references instead of hard-coded values
11+
12+
### What's Broken
13+
-**System preference (prefers-color-scheme) not respected on first visit in real browser**
14+
- Test passes but real usage fails
15+
- Opening site in new incognito window with dark mode preference shows light theme
16+
- Google.com correctly shows dark, but our site doesn't
17+
18+
-**Theme picker buttons should show their own theme's colors**
19+
- Dark theme button should use `--dark-color-*` variables
20+
- Default theme button should use `--light-color-*` variables
21+
- Currently all buttons use light theme colors (partially fixed but needs completion)
22+
23+
## Root Cause Analysis
24+
25+
### System Preference Issue
26+
27+
The problem is a **race condition** between:
28+
1. HEAD inline script (synchronous) - correctly sets `data-theme="dark"` based on `prefers-color-scheme`
29+
2. `persistentAtom` restore (asynchronous) - overwrites theme back to 'default'
30+
31+
**Sequence of events:**
32+
```
33+
1. HEAD script runs → checks localStorage (empty) → checks prefersDark=true → sets data-theme="dark" ✓
34+
2. persistentAtom initializes with default value 'default'
35+
3. Our init code runs with setTimeout(100ms)
36+
4. persistentAtom's restore() completes (async) → fires .listen() → overwrites to 'default' ✗
37+
```
38+
39+
**Current fix attempt:**
40+
- Using `isInitialized` flag to prevent `.listen()` from applying themes until init completes
41+
- Using `setTimeout(100ms)` to delay init until after `persistentAtom.restore()` completes
42+
- **Problem:** The timing is unreliable - 100ms might not be enough on slower devices
43+
44+
**Test vs Reality:**
45+
- Playwright test passes because it's using `emulateMedia({ colorScheme: 'dark' })`
46+
- Real browser behavior is different - the race condition manifests differently
47+
- Test needs to be improved to catch this real-world bug
48+
49+
## Files Modified
50+
51+
### Theme Initialization
52+
- `src/components/Scripts/state/store/themes.ts` (lines 127-180)
53+
- Changed from `.subscribe()` to `.listen()` to avoid immediate firing
54+
- Added `isInitialized` flag to gate theme applications
55+
- Added `setTimeout(100)` to wait for `persistentAtom.restore()`
56+
- **HAS DEBUG LOGGING** - needs to be removed before commit
57+
58+
### Theme Picker UI
59+
- `src/components/ThemePicker/Themes.astro` (lines 97-120)
60+
- **NOT YET FIXED** - still needs to map theme.id to color prefix
61+
- Should use `--${colorPrefix}-color-*` variables per button
62+
63+
### HEAD Script
64+
- `src/components/Head/index.astro` (lines 56-63)
65+
- Correctly checks `prefers-color-scheme` and sets `data-theme`
66+
- Logic: stored theme (if not 'default') > system preference > 'default'
67+
68+
### CSS Architecture
69+
- `src/styles/themes.css`
70+
- ✅ Lines 65-105: `@theme inline` refactored to use `var()` references
71+
- ✅ All theme-specific colors defined at `:root` level
72+
- Has `--light-color-*`, `--dark-color-*`, and base `--color-*` variables
73+
74+
## Next Steps
75+
76+
### High Priority
77+
1. **Fix system preference detection**
78+
- Option A: Find more reliable way to detect when `persistentAtom.restore()` completes
79+
- Option B: Use `MutationObserver` to watch for theme changes from restore
80+
- Option C: Initialize theme BEFORE importing `persistentAtom`
81+
- Option D: Use regular `atom` for store, manually sync to localStorage after restore completes
82+
- Option E: Don't rely on setTimeout - use `requestIdleCallback` or similar
83+
84+
2. **Fix theme picker button colors**
85+
- Complete the Themes.astro fix to show each theme's own colors
86+
- Map `theme.id` to color variable prefix: 'default' → 'light', 'dark' → 'dark'
87+
- Update color swatches to use theme-specific variables
88+
89+
3. **Remove debug logging**
90+
- `src/components/Scripts/state/store/themes.ts` has console.log statements
91+
- Clean these up before final commit
92+
93+
### Test Improvements
94+
4. **Make test match real browser behavior**
95+
- Current test uses `emulateMedia()` which might not trigger same race condition
96+
- Consider testing with actual localStorage clearing and page reload
97+
- Add test that validates theme immediately on page load (before JS runs)
98+
99+
## Technical Constraints
100+
101+
- **MUST use `persistentAtom`** - required for View Transitions to maintain theme across navigations
102+
- **CANNOT use regular `atom`** - will lose persistence across page navigations
103+
- HEAD script must run synchronously to prevent FOUC (Flash of Unstyled Content)
104+
- Theme must be applied before page renders (critical for UX)
105+
106+
## Code Locations
107+
108+
- Theme store: `src/components/Scripts/state/store/themes.ts`
109+
- Theme picker UI: `src/components/ThemePicker/Themes.astro`
110+
- Theme picker element: `src/components/ThemePicker/theme-picker-element.ts`
111+
- HEAD script: `src/components/Head/index.astro` (lines 56-63)
112+
- CSS themes: `src/styles/themes.css`
113+
- E2E tests: `test/e2e/specs/04-components/theme-picker.spec.ts` (line 134 is failing test)
114+
115+
## Questions to Answer
116+
117+
1. When exactly does `persistentAtom.restore()` complete?
118+
2. Is there an event or promise we can wait for?
119+
3. Should we implement our own localStorage persistence instead of using `persistentAtom`?
120+
4. Can we leverage the `@media (prefers-color-scheme: dark)` CSS to avoid needing JS for system preference?
121+
122+
## Useful Context
123+
124+
- The `@media (prefers-color-scheme: dark)` CSS rule at lines 337-373 in themes.css correctly applies dark theme
125+
- This CSS works WITHOUT JavaScript
126+
- The issue is the JS is overriding this CSS by setting `data-theme="default"`
127+
- Maybe we should NOT set `data-theme` at all when using system preference?

0 commit comments

Comments
 (0)