51 lines
3.9 KiB
Markdown
51 lines
3.9 KiB
Markdown
# UX-002 deterministic theme state
|
||
|
||
Updated: 2026-08-15
|
||
|
||
Status: `IMPLEMENTED — NOT VERIFIED`. Automated, build and local browser checks pass. Production and authenticated multi-user browser checks remain.
|
||
|
||
## Confirmed root cause
|
||
|
||
The theme preference was keyed by the last `authUserKey`, but login completion emitted the general authentication event before `/auth/me` stored the new user key. `Shell` then stored that key with event emission disabled. Theme state therefore continued using the anonymous preference until a refresh, when the user key was already present and the page appeared to switch theme randomly. Logout had a similar asynchronous boundary.
|
||
|
||
Theme changes also keyed `CssVarsProvider` and captured `themeMode` in the router memo. A preference change remounted the provider and recreated the router, risking loss of in-page state even though the URL did not intentionally change. MUI also retained its own default local-storage mode, creating another potential source of truth.
|
||
|
||
## Implemented contract
|
||
|
||
- `jobtracker.themeMode` is the single browser preference. Login, logout and delayed `/auth/me` resolution cannot change it.
|
||
- The first read migrates the former current-user/anonymous value into the canonical key, preserving existing choices.
|
||
- Explicit Light and Dark ignore operating-system changes. Only System resolves through the current media query.
|
||
- Canonical `storage` events update another tab without writing back. Auth and unrelated storage events are ignored.
|
||
- MUI mode is changed in place through its color-scheme context with its private persistence disabled. The app/router tree is not keyed or recreated by theme changes.
|
||
- A Next `beforeInteractive` bootstrap applies the same canonical/migration/System resolution before client application paint.
|
||
- Semantic Alert variants use explicit theme severity surfaces and foreground tokens; dark warning/error/info/success text no longer inherits dark-on-dark defaults.
|
||
- Settings tabs are scrollable at narrow widths; this removes the mobile overflow discovered during the required theme browser pass.
|
||
|
||
No backend, database, dependency, entitlement or production configuration changed.
|
||
|
||
## Verification
|
||
|
||
- Focused deterministic theme and confirmation suites: 8/8.
|
||
- Full frontend: 48/48 suites and 172/172 tests.
|
||
- Production frontend build/TypeScript and `git diff --check`: pass.
|
||
- Browser: explicit Light persisted across Settings → Dashboard navigation and refresh; explicit Dark switched without navigation; System selected the browser's dark preference; a second tab inherited Dark and changing it to Light updated the first tab without reload.
|
||
- Browser widths: 375, 768 and 1440; document width did not exceed the viewport after the tabs correction. Light 375/768 and Dark 1440 evidence is retained.
|
||
- Browser console warnings/errors after the final interactive pass: none. Development HMR emitted transient module-update messages while source files were being edited; they were not present in the captured tab diagnostics and the clean production build passes.
|
||
|
||
## Remaining gates
|
||
|
||
- Refresh/navigation and migration are automated; the revised canonical store still needs a real authenticated browser refresh pass before production verification.
|
||
- A real operating-system preference-change event was tested at the resolver/provider boundary, not by changing the host OS during browser automation.
|
||
- Production deployment/browser smoke is not authorized/configured.
|
||
|
||
## Evidence
|
||
|
||
- Screenshots: `docs/audits/evidence/ux-002/`
|
||
- Commands/results: `docs/audits/verification-log.md` V-111–V-113
|
||
- Tests: `job-tracker-ui/src/theme-state.test.tsx`
|
||
- Initial implementation commit: `11734ee`; canonical-store/contrast correction: pending this change set.
|
||
|
||
## Rollback
|
||
|
||
Revert the canonical-store correction to restore account-scoped lookup. No destructive data migration is required; old `themeMode:<user>` values remain untouched and the canonical key can be removed independently.
|