aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-01-20 04:41:41 -0800
committerFuwn <[email protected]>2026-01-20 04:41:41 -0800
commit90d9451d7cdae596adf42362fed6c3fa54884193 (patch)
tree719eb1ac3eec89a7d0dd04dca43d8dcad2b0019f
parentfix: Prevent flash of light mode on page load (diff)
downloadkaze-90d9451d7cdae596adf42362fed6c3fa54884193.tar.xz
kaze-90d9451d7cdae596adf42362fed6c3fa54884193.zip
fix: Prevent flash of light mode on page load
- Default to dark mode by setting class="dark" on html element - Move theme CSS before external stylesheet to prevent flash - Only remove dark class if light mode is explicitly preferred - Add !important to theme variable overrides for precedence
-rw-r--r--internal/server/templates/index.html19
-rw-r--r--internal/theme/theme.go50
2 files changed, 36 insertions, 33 deletions
diff --git a/internal/server/templates/index.html b/internal/server/templates/index.html
index 025df89..1ba3177 100644
--- a/internal/server/templates/index.html
+++ b/internal/server/templates/index.html
@@ -1,12 +1,19 @@
<!DOCTYPE html>
-<html lang="en">
+<html lang="en" class="dark">
<head>
<script>
// Theme detection - runs immediately before any rendering
- if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
- document.documentElement.classList.add('dark');
+ // Default is dark (set on html element above), switch to light only if explicitly set
+ if (localStorage.theme === 'light' || (localStorage.theme !== 'dark' && window.matchMedia('(prefers-color-scheme: light)').matches)) {
+ document.documentElement.classList.remove('dark');
}
</script>
+ {{if .ThemeCSS}}
+ <style>
+ /* OpenCode Theme - Loaded before external CSS to prevent flash */
+{{.ThemeCSS}}
+ </style>
+ {{end}}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Site.Name}}</title>
@@ -17,12 +24,6 @@
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🎐</text></svg>">
{{end}}
<link rel="stylesheet" href="/static/style.css">
- {{if .ThemeCSS}}
- <style>
- /* OpenCode Theme */
-{{.ThemeCSS}}
- </style>
- {{end}}
</head>
<body class="bg-neutral-50 dark:bg-neutral-950 text-neutral-900 dark:text-neutral-100 min-h-screen font-mono">
<div class="max-w-4xl mx-auto px-4 py-8 sm:py-12">
diff --git a/internal/theme/theme.go b/internal/theme/theme.go
index 9e3d30b..691afa0 100644
--- a/internal/theme/theme.go
+++ b/internal/theme/theme.go
@@ -185,6 +185,7 @@ func GetColorMapping() map[string]string {
// GenerateVariableOverrides generates CSS that maps OpenCode theme to Kaze's CSS variables
// This is the proper approach - override the root CSS variables that style.css uses
+// Uses !important to ensure theme overrides take precedence over external CSS
func (t *ResolvedTheme) GenerateVariableOverrides() string {
if t == nil {
return ""
@@ -192,51 +193,52 @@ func (t *ResolvedTheme) GenerateVariableOverrides() string {
return `
/* OpenCode Theme - Override Kaze CSS Variables */
+/* Uses !important to prevent flash when external CSS loads */
/* Light mode - uses theme's light colors */
:root, :root.light {
/* Background colors */
- --bg-primary: var(--theme-background);
- --bg-secondary: var(--theme-background-panel);
- --bg-tertiary: var(--theme-background-element);
+ --bg-primary: var(--theme-background) !important;
+ --bg-secondary: var(--theme-background-panel) !important;
+ --bg-tertiary: var(--theme-background-element) !important;
/* Border color */
- --border-color: var(--theme-border);
+ --border-color: var(--theme-border) !important;
/* Text colors */
- --text-primary: var(--theme-text);
- --text-secondary: var(--theme-text-muted);
- --text-tertiary: var(--theme-text-muted);
- --text-dim: var(--theme-border);
+ --text-primary: var(--theme-text) !important;
+ --text-secondary: var(--theme-text-muted) !important;
+ --text-tertiary: var(--theme-text-muted) !important;
+ --text-dim: var(--theme-border) !important;
/* Status colors */
- --status-ok: var(--theme-success);
- --status-warn: var(--theme-warning);
- --status-error: var(--theme-error);
- --status-unknown: var(--theme-text-muted);
+ --status-ok: var(--theme-success) !important;
+ --status-warn: var(--theme-warning) !important;
+ --status-error: var(--theme-error) !important;
+ --status-unknown: var(--theme-text-muted) !important;
}
/* Dark mode - uses theme's dark colors */
:root.dark {
/* Background colors */
- --bg-primary: var(--theme-background);
- --bg-secondary: var(--theme-background-panel);
- --bg-tertiary: var(--theme-background-element);
+ --bg-primary: var(--theme-background) !important;
+ --bg-secondary: var(--theme-background-panel) !important;
+ --bg-tertiary: var(--theme-background-element) !important;
/* Border color */
- --border-color: var(--theme-border);
+ --border-color: var(--theme-border) !important;
/* Text colors */
- --text-primary: var(--theme-text);
- --text-secondary: var(--theme-text-muted);
- --text-tertiary: var(--theme-text-muted);
- --text-dim: var(--theme-border);
+ --text-primary: var(--theme-text) !important;
+ --text-secondary: var(--theme-text-muted) !important;
+ --text-tertiary: var(--theme-text-muted) !important;
+ --text-dim: var(--theme-border) !important;
/* Status colors */
- --status-ok: var(--theme-success);
- --status-warn: var(--theme-warning);
- --status-error: var(--theme-error);
- --status-unknown: var(--theme-text-muted);
+ --status-ok: var(--theme-success) !important;
+ --status-warn: var(--theme-warning) !important;
+ --status-error: var(--theme-error) !important;
+ --status-unknown: var(--theme-text-muted) !important;
}
`
}