aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config.example.yaml4
-rw-r--r--internal/config/config.go6
-rw-r--r--internal/server/server.go2
-rw-r--r--internal/server/static/style.css123
-rw-r--r--internal/server/templates/index.html30
-rw-r--r--internal/theme/theme.go58
6 files changed, 89 insertions, 134 deletions
diff --git a/config.example.yaml b/config.example.yaml
index bf3ff0f..9dab8b2 100644
--- a/config.example.yaml
+++ b/config.example.yaml
@@ -48,10 +48,6 @@ display:
# "UTC" - Use UTC timezone
# "America/New_York" - Use specific IANA timezone (e.g., "Europe/London", "Asia/Tokyo")
timezone: "Browser"
-
- # Show the theme toggle button (true by default)
- # Set to false to hide the light/dark mode toggle
- show_theme_toggle: true
# Monitor groups
groups:
diff --git a/internal/config/config.go b/internal/config/config.go
index 68a88f7..dfd403a 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -30,8 +30,6 @@ type DisplayConfig struct {
PingFixedSlots bool `yaml:"ping_fixed_slots"`
// Timezone for display (e.g., "UTC", "America/New_York", "Local")
Timezone string `yaml:"timezone"`
- // ShowThemeToggle controls whether to show the theme toggle button (defaults to true)
- ShowThemeToggle *bool `yaml:"show_theme_toggle"`
}
// SiteConfig contains site metadata
@@ -204,10 +202,6 @@ func (c *Config) applyDefaults() {
if c.Display.Timezone == "" {
c.Display.Timezone = "Local"
}
- if c.Display.ShowThemeToggle == nil {
- defaultShow := true
- c.Display.ShowThemeToggle = &defaultShow
- }
// Apply group defaults
for i := range c.Groups {
diff --git a/internal/server/server.go b/internal/server/server.go
index 8d491dc..6b5ded3 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -126,7 +126,6 @@ type PageData struct {
LastUpdatedTooltip string // JSON data for last updated tooltip
TickMode string // ping, minute, hour, day
TickCount int
- ShowThemeToggle bool
Timezone string // Timezone for display
UseBrowserTimezone bool // Use client-side timezone conversion
ThemeCSS template.CSS // OpenCode theme CSS (safe CSS)
@@ -210,7 +209,6 @@ func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
Site: s.config.Site,
TickMode: s.config.Display.TickMode,
TickCount: s.config.Display.TickCount,
- ShowThemeToggle: s.config.Display.ShowThemeToggle != nil && *s.config.Display.ShowThemeToggle,
Timezone: s.config.Display.Timezone,
UseBrowserTimezone: s.config.Display.Timezone == "Browser",
ThemeCSS: themeCSS,
diff --git a/internal/server/static/style.css b/internal/server/static/style.css
index 3f197a7..94a7e11 100644
--- a/internal/server/static/style.css
+++ b/internal/server/static/style.css
@@ -33,35 +33,22 @@ html {
--status-unknown: #555555;
}
-.dark {
- --bg-primary: #070707;
- --bg-secondary: #0f0f0f;
- --bg-tertiary: #1a1a1a;
- --border-color: #2a2a2a;
- --text-primary: #ffffff;
- --text-secondary: #999999;
- --text-tertiary: #666666;
- --text-dim: #444444;
- --status-ok: #d0d0d0;
- --status-warn: #a0a0a0;
- --status-error: #808080;
- --status-unknown: #555555;
-}
-
/* Light mode - inverse terminal */
-:root:not(.dark) {
- --bg-primary: #ffffff;
- --bg-secondary: #f8f8f8;
- --bg-tertiary: #f0f0f0;
- --border-color: #e0e0e0;
- --text-primary: #000000;
- --text-secondary: #666666;
- --text-tertiary: #999999;
- --text-dim: #bbbbbb;
- --status-ok: #333333;
- --status-warn: #555555;
- --status-error: #777777;
- --status-unknown: #aaaaaa;
+@media (prefers-color-scheme: light) {
+ :root {
+ --bg-primary: #ffffff;
+ --bg-secondary: #f8f8f8;
+ --bg-tertiary: #f0f0f0;
+ --border-color: #e0e0e0;
+ --text-primary: #000000;
+ --text-secondary: #666666;
+ --text-tertiary: #999999;
+ --text-dim: #bbbbbb;
+ --status-ok: #333333;
+ --status-warn: #555555;
+ --status-error: #777777;
+ --status-unknown: #aaaaaa;
+ }
}
/* Font */
@@ -152,49 +139,53 @@ body {
.border-red-900 { border-color: var(--border-color); }
/* Dark mode overrides */
-.dark .dark\:bg-neutral-800 { background-color: var(--bg-tertiary); }
-.dark .dark\:bg-neutral-900 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-neutral-900\/50 { background-color: rgba(15, 15, 15, 0.5); }
-.dark .dark\:bg-neutral-950 { background-color: var(--bg-primary); }
-.dark .dark\:bg-emerald-900\/50 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-emerald-950\/30 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-yellow-900\/50 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-yellow-950\/20 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-yellow-950\/30 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-red-900\/50 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-red-950\/30 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-blue-900\/50 { background-color: var(--bg-secondary); }
-.dark .dark\:bg-orange-900\/50 { background-color: var(--bg-secondary); }
-
-.dark .dark\:text-neutral-100 { color: var(--text-primary); }
-.dark .dark\:text-neutral-300 { color: var(--text-secondary); }
-.dark .dark\:text-neutral-400 { color: var(--text-secondary); }
-.dark .dark\:text-neutral-500 { color: var(--text-tertiary); }
-.dark .dark\:text-emerald-300 { color: var(--status-ok); }
-.dark .dark\:text-emerald-400 { color: var(--status-ok); }
-.dark .dark\:text-yellow-300 { color: var(--status-warn); }
-.dark .dark\:text-yellow-400 { color: var(--status-warn); }
-.dark .dark\:text-red-400 { color: var(--status-error); }
-.dark .dark\:text-blue-300 { color: var(--status-ok); }
-.dark .dark\:text-orange-300 { color: var(--status-warn); }
-
-.dark .dark\:border-neutral-800 { border-color: var(--border-color); }
-.dark .dark\:border-emerald-900 { border-color: var(--border-color); }
-.dark .dark\:border-yellow-900 { border-color: var(--border-color); }
-.dark .dark\:border-red-900 { border-color: var(--border-color); }
-
-.dark .dark\:divide-neutral-800 > :not([hidden]) ~ :not([hidden]) { border-color: var(--border-color); }
-
-.dark .dark\:hover\:bg-neutral-800:hover { background-color: #1f1f1f; }
-.dark .dark\:hover\:bg-neutral-900\/50:hover { background-color: rgba(15, 15, 15, 0.5); }
-.dark .dark\:hover\:text-neutral-100:hover { color: var(--text-primary); }
+@media (prefers-color-scheme: dark) {
+ .dark\:bg-neutral-800 { background-color: var(--bg-tertiary); }
+ .dark\:bg-neutral-900 { background-color: var(--bg-secondary); }
+ .dark\:bg-neutral-900\/50 { background-color: rgba(15, 15, 15, 0.5); }
+ .dark\:bg-neutral-950 { background-color: var(--bg-primary); }
+ .dark\:bg-emerald-900\/50 { background-color: var(--bg-secondary); }
+ .dark\:bg-emerald-950\/30 { background-color: var(--bg-secondary); }
+ .dark\:bg-yellow-900\/50 { background-color: var(--bg-secondary); }
+ .dark\:bg-yellow-950\/20 { background-color: var(--bg-secondary); }
+ .dark\:bg-yellow-950\/30 { background-color: var(--bg-secondary); }
+ .dark\:bg-red-900\/50 { background-color: var(--bg-secondary); }
+ .dark\:bg-red-950\/30 { background-color: var(--bg-secondary); }
+ .dark\:bg-blue-900\/50 { background-color: var(--bg-secondary); }
+ .dark\:bg-orange-900\/50 { background-color: var(--bg-secondary); }
+
+ .dark\:text-neutral-100 { color: var(--text-primary); }
+ .dark\:text-neutral-300 { color: var(--text-secondary); }
+ .dark\:text-neutral-400 { color: var(--text-secondary); }
+ .dark\:text-neutral-500 { color: var(--text-tertiary); }
+ .dark\:text-emerald-300 { color: var(--status-ok); }
+ .dark\:text-emerald-400 { color: var(--status-ok); }
+ .dark\:text-yellow-300 { color: var(--status-warn); }
+ .dark\:text-yellow-400 { color: var(--status-warn); }
+ .dark\:text-red-400 { color: var(--status-error); }
+ .dark\:text-blue-300 { color: var(--status-ok); }
+ .dark\:text-orange-300 { color: var(--status-warn); }
+
+ .dark\:border-neutral-800 { border-color: var(--border-color); }
+ .dark\:border-emerald-900 { border-color: var(--border-color); }
+ .dark\:border-yellow-900 { border-color: var(--border-color); }
+ .dark\:border-red-900 { border-color: var(--border-color); }
+
+ .dark\:divide-neutral-800 > :not([hidden]) ~ :not([hidden]) { border-color: var(--border-color); }
+
+ .dark\:hover\:bg-neutral-800:hover { background-color: #1f1f1f; }
+ .dark\:hover\:bg-neutral-900\/50:hover { background-color: rgba(15, 15, 15, 0.5); }
+ .dark\:hover\:text-neutral-100:hover { color: var(--text-primary); }
+}
/* Display */
.block { display: block; }
.hidden { display: none; }
.flex { display: flex; }
-.dark .dark\:block { display: block; }
-.dark .dark\:hidden { display: none; }
+@media (prefers-color-scheme: dark) {
+ .dark\:block { display: block; }
+ .dark\:hidden { display: none; }
+}
/* Flexbox */
.flex-1 { flex: 1 1 0%; }
diff --git a/internal/server/templates/index.html b/internal/server/templates/index.html
index 1ba3177..9d98b44 100644
--- a/internal/server/templates/index.html
+++ b/internal/server/templates/index.html
@@ -1,13 +1,6 @@
<!DOCTYPE html>
-<html lang="en" class="dark">
+<html lang="en">
<head>
- <script>
- // Theme detection - runs immediately before any rendering
- // 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 */
@@ -39,16 +32,7 @@
<p class="text-sm text-neutral-500 dark:text-neutral-400">{{.Site.Description}}</p>
</div>
</div>
- {{if .ShowThemeToggle}}
- <button onclick="toggleTheme()" class="p-2 rounded-md hover:bg-neutral-200 dark:hover:bg-neutral-800 transition-colors" aria-label="Toggle theme">
- <svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/>
- </svg>
- <svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/>
- </svg>
- </button>
- {{end}}
+
</div>
</header>
@@ -210,16 +194,6 @@
<div id="tooltip" class="tooltip"></div>
<script>
- function toggleTheme() {
- if (document.documentElement.classList.contains('dark')) {
- document.documentElement.classList.remove('dark');
- localStorage.theme = 'light';
- } else {
- document.documentElement.classList.add('dark');
- localStorage.theme = 'dark';
- }
- }
-
// Group collapse/expand functionality
function toggleGroup(groupName) {
const content = document.querySelector('[data-group-content="' + groupName + '"]');
diff --git a/internal/theme/theme.go b/internal/theme/theme.go
index 691afa0..1fc6600 100644
--- a/internal/theme/theme.go
+++ b/internal/theme/theme.go
@@ -117,8 +117,8 @@ func (t *ResolvedTheme) GenerateCSS() string {
var css strings.Builder
- // Generate CSS variables for dark mode
- css.WriteString(":root.dark {\n")
+ // Generate CSS variables for dark mode (default)
+ css.WriteString(":root {\n")
for key, color := range t.Dark {
cssVar := toCSSVariableName(key)
css.WriteString(fmt.Sprintf(" %s: %s;\n", cssVar, color))
@@ -126,12 +126,12 @@ func (t *ResolvedTheme) GenerateCSS() string {
css.WriteString("}\n\n")
// Generate CSS variables for light mode
- css.WriteString(":root, :root.light {\n")
+ css.WriteString("@media (prefers-color-scheme: light) {\n :root {\n")
for key, color := range t.Light {
cssVar := toCSSVariableName(key)
- css.WriteString(fmt.Sprintf(" %s: %s;\n", cssVar, color))
+ css.WriteString(fmt.Sprintf(" %s: %s;\n", cssVar, color))
}
- css.WriteString("}\n")
+ css.WriteString(" }\n}\n")
return css.String()
}
@@ -195,8 +195,8 @@ func (t *ResolvedTheme) GenerateVariableOverrides() string {
/* 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 {
+/* Dark mode (default) - uses theme's dark colors */
+:root {
/* Background colors */
--bg-primary: var(--theme-background) !important;
--bg-secondary: var(--theme-background-panel) !important;
@@ -218,27 +218,29 @@ func (t *ResolvedTheme) GenerateVariableOverrides() string {
--status-unknown: var(--theme-text-muted) !important;
}
-/* Dark mode - uses theme's dark colors */
-:root.dark {
- /* Background colors */
- --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) !important;
-
- /* Text colors */
- --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) !important;
- --status-warn: var(--theme-warning) !important;
- --status-error: var(--theme-error) !important;
- --status-unknown: var(--theme-text-muted) !important;
+/* Light mode - uses theme's light colors */
+@media (prefers-color-scheme: light) {
+ :root {
+ /* Background colors */
+ --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) !important;
+
+ /* Text colors */
+ --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) !important;
+ --status-warn: var(--theme-warning) !important;
+ --status-error: var(--theme-error) !important;
+ --status-unknown: var(--theme-text-muted) !important;
+ }
}
`
}