feat(ui): light/dark/system theme switching + per-user pref (LC-191) #237
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-191-theme-switching"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Theme switching on top of the LC-190 token foundation: light / dark / system, a per-user server-side preference, and a no-flash client bootstrap.
What lands
users.themecolumn (auth migration 0025, mirroringusers.locale) -system/light/dark, NULL = system. Threaded throughUserRecord/User+set_user_theme; 0025 added to the 22 hand-rolled auth-pool test files (migration-list drift, per CLAUDE.md).[data-theme="dark"]inmain.css(slate-based,color-scheme: dark) using the LC-190 token contract.base.html): a synchronous script placed before the stylesheets sets<html data-theme>from thelc-themecookie -> localStorage ->prefers-color-scheme, before CSS paints. Keeps<meta theme-color>in step, follows OS changes while on system, and exposeswindow.__lcSetThemefor the picker.resolve_localemiddleware stamps thelc-themecookie fromusers.themewhen it drifts, so a fresh device's first authed response sets the cookie the bootstrap reads.onchangeapplies instantly via__lcSetTheme+ remembers on-device; the form POST (/settings/appearance) persists server-side.User::theme_or_system()marks the selected option.Scope note
The dark palette recolors the whole app once LC-193 migrates templates onto the tokens. Until then the switch recolors the
<body>and any already-tokenized surfaces; un-migrated panels still use rawslate/white. So dark mode is functional-but-partial on this branch by design (the chain is LC-190 -> LC-191 -> LC-193). Merge order is yours; if a polished dark mode should land all at once, hold this until LC-193.Verification
just test+just test-saasgreen;cargo fmt --allclean;i18n_cataloggate passes (newsettings-appearance/theme-*keys in en + esmain.ftl). Bootstrap is synchronous + pre-stylesheet (no flash); cookie isSameSite=Lax, 1-year, path=/.