Apply the theme from PageSettings in JS, before the first paint

wwwroot/js/theme.js now picks the theme. It sets data-theme (neo-light or
neo-dark, or none when the OS decides) and the hue and chroma variables on
<html>, using the same flag precedence as the old CascadingState branches. It
also keeps <meta name="theme-color"> in sync. Because it runs in <head> from the
settings saved in localStorage, a reload no longer flashes the default palette
before Blazor boots. With system theming it also follows the OS when the colour
scheme changes.

CascadingState loses its ~200 lines of generated <style> and <Meta> tags. It now
calls AppStatusService.ApplyTheme on load and whenever the settings change. The
--fa-* icon colour variables are gone; nothing read them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-03 11:29:31 +02:00
1 parent 5b5fd62233
commit 8e03d12a3e
4 files changed
+64 -203

No files matched your search

+2
View File
@@ -8,6 +8,8 @@
<base href="/" />
<link href="vendor/ionicons.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<meta name="theme-color" content="hsl(0 0% 88%)" />
<script src="js/theme.js"></script>
<link href="manifest.json" rel="manifest" />
<link rel="apple-touch-icon" sizes="512x512" href="imgs/icon-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="imgs/icon-192.png" />
+52
View File
@@ -0,0 +1,52 @@
// Applies PageSettings to the page. It picks the daisyUI theme from Styles/theme.css and sets the hue and chroma
// variables that theme derives every colour from. It loads in <head> right after css/app.css and applies the
// settings saved in localStorage before the first paint. Once Blazor runs, CascadingState calls neoTheme.apply
// through AppStatusService.ApplyTheme whenever the settings change.
(() => {
const root = document.documentElement
let current = null
// The same precedence the old CascadingState <style> block had:
// - System theming leaves data-theme off, so daisyUI's prefersdark rule decides.
// - Otherwise dark grey, then light grey, then dark mode, then light.
// - Either grey flag drops the chroma to 0.
const apply = (settings) => {
current = settings || {}
const system = current.preferSystemTheming ?? true
const darkGray = current.themeIsDarkGray ?? false
const lightGray = current.themeIsLightGray ?? true
const dark = current.themeIsDarkMode ?? false
const theme = system ? null : darkGray ? "neo-dark" : lightGray ? "neo-light" : dark ? "neo-dark" : "neo-light"
if (theme)
root.setAttribute("data-theme", theme)
else
root.removeAttribute("data-theme")
root.style.setProperty("--neo-hue-light", current.lightThemeIndexColour ?? 25)
root.style.setProperty("--neo-hue-dark", current.darkThemeIndexColour ?? 215)
root.style.setProperty("--neo-chroma", darkGray || lightGray ? 0 : 1)
// daisyUI paints the root with base-100, so its computed background is the resolved theme colour.
let meta = document.querySelector('meta[name="theme-color"]')
if (!meta) {
meta = document.createElement("meta")
meta.name = "theme-color"
document.head.appendChild(meta)
}
meta.content = getComputedStyle(root).backgroundColor
}
try {
const cache = JSON.parse(localStorage.getItem("PublicCacheData") || "null")
apply(cache && cache.pageSettings)
} catch (e) {
console.error("theme.js", e)
apply(null)
}
// With system theming the OS can switch between light and dark at any time.
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => apply(current))
window.neoTheme = { apply }
})()