feat: implement dark mode toggle across application
- Added dark mode detection and application logic in HTML files (dashboard.html, kioskedit.html, live.html, rulesets.html, sessions.html). - Introduced a theme toggle button for user interaction. - Created a new theme-toggle.js script to manage dark mode state and persistence using localStorage. - Updated CSS styles to support dark mode with appropriate color variables and transitions. - Enhanced user experience by preventing flash of unstyled content during theme initialization.
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -18,4 +18,6 @@ Thumbs.db
|
||||
**/tsconfig.tsbuildinfo
|
||||
.eslintcache
|
||||
|
||||
.venv/
|
||||
.venv/
|
||||
|
||||
.claude/
|
||||
File diff suppressed because one or more lines are too long
@@ -7,7 +7,18 @@
|
||||
|
||||
<script src='https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js'></script>
|
||||
<link href='https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css' rel='stylesheet' />
|
||||
<link rel="stylesheet" href="../static/styles/style.css">
|
||||
<link rel="stylesheet" href="../static/styles/kiosk.css">
|
||||
<script>
|
||||
// Detect and apply dark mode immediately to prevent flash
|
||||
const THEME_KEY = 'meb-console-theme';
|
||||
const saved = localStorage.getItem(THEME_KEY);
|
||||
const isDark = saved ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark-mode');
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -37,6 +48,7 @@
|
||||
<p id="cardCount">0 cards</p>
|
||||
|
||||
<button id="editBtn">Edit</button>
|
||||
<button id="theme-toggle-btn" title="Dark Mode" style="padding: 8px 12px; font-size: 18px;">🌙</button>
|
||||
<button id="addCardBtn" title="Aggiungi Widget">+</button>
|
||||
<button id="addMapBtn" title="Aggiungi Mappa">Map</button>
|
||||
|
||||
@@ -555,4 +567,15 @@ ws.onclose = () => {
|
||||
|
||||
</script>
|
||||
|
||||
<script src="../static/theme-toggle.js"></script>
|
||||
<script>
|
||||
// Theme toggle button event listener
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeBtn = document.getElementById('theme-toggle-btn');
|
||||
if (themeBtn) {
|
||||
themeBtn.addEventListener('click', toggleDarkMode);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -11,6 +11,16 @@
|
||||
.expanded-chart-container { display: none; }
|
||||
.comparison-sidebar { display: none; }
|
||||
</style>
|
||||
<script>
|
||||
// Detect and apply dark mode immediately to prevent flash
|
||||
const THEME_KEY = 'meb-console-theme';
|
||||
const saved = localStorage.getItem(THEME_KEY);
|
||||
const isDark = saved ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark-mode');
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -50,6 +60,7 @@
|
||||
<p class="last-update" id="lastUpdateText">In attesa di dati...</p>
|
||||
</div>
|
||||
<div class="profile">
|
||||
<button id="theme-toggle-btn" title="Dark Mode" style="padding: 8px 12px; font-size: 18px;">🌙</button>
|
||||
<p id="sensorName">Sensore</p>
|
||||
<button id="changeSessionBtn">Cambia sessione</button>
|
||||
</div>
|
||||
@@ -777,3 +788,14 @@ document.getElementById('saveSessionLabelBtn').onclick = async () => {
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<script src="/static/theme-toggle.js"></script>
|
||||
<script>
|
||||
// Theme toggle button event listener
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeBtn = document.getElementById('theme-toggle-btn');
|
||||
if (themeBtn) {
|
||||
themeBtn.addEventListener('click', toggleDarkMode);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
<link rel="stylesheet" href="/static/styles/style.css">
|
||||
<link rel="stylesheet" href="/static/styles/rulesets.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script>
|
||||
// Detect and apply dark mode immediately to prevent flash
|
||||
const THEME_KEY = 'meb-console-theme';
|
||||
const saved = localStorage.getItem(THEME_KEY);
|
||||
const isDark = saved ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark-mode');
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -24,6 +34,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="rs-header-right">
|
||||
<button id="theme-toggle-btn" title="Dark Mode" style="padding: 8px 12px; font-size: 18px;">🌙</button>
|
||||
<span class="rs-saving" id="savingIndicator">Salvato</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -594,5 +605,16 @@ function flash(text, elId = 'savingIndicator') {
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => loadRules());
|
||||
|
||||
</script>
|
||||
|
||||
<script src="/static/theme-toggle.js"></script>
|
||||
<script>
|
||||
// Theme toggle button event listener
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeBtn = document.getElementById('theme-toggle-btn');
|
||||
if (themeBtn) {
|
||||
themeBtn.addEventListener('click', toggleDarkMode);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -244,6 +244,16 @@
|
||||
.map-bar .filter button { color: #cbd5e1; }
|
||||
.map-bar .filter button.active { background: #3b82f6; color: #fff; }
|
||||
</style>
|
||||
<script>
|
||||
// Detect and apply dark mode immediately to prevent flash
|
||||
const THEME_KEY = 'meb-console-theme';
|
||||
const saved = localStorage.getItem(THEME_KEY);
|
||||
const isDark = saved ? saved === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
if (isDark) {
|
||||
document.documentElement.classList.add('dark-mode');
|
||||
document.body.classList.add('dark-mode');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -279,6 +289,7 @@
|
||||
<h1>Sessioni</h1>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<button id="theme-toggle-btn" title="Dark Mode" style="padding: 8px 12px; font-size: 18px;">🌙</button>
|
||||
<span id="sessionNameDisplay"></span>
|
||||
<span id="sessionMetaDisplay"></span>
|
||||
</div>
|
||||
@@ -918,5 +929,16 @@ document.getElementById('changeSessionBtn').onclick = () => {
|
||||
// --- Init ---
|
||||
document.addEventListener('DOMContentLoaded', loadSessionsList);
|
||||
</script>
|
||||
|
||||
<script src="/static/theme-toggle.js"></script>
|
||||
<script>
|
||||
// Theme toggle button event listener
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const themeBtn = document.getElementById('theme-toggle-btn');
|
||||
if (themeBtn) {
|
||||
themeBtn.addEventListener('click', toggleDarkMode);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -20,6 +20,53 @@
|
||||
--radius-lg: 12px;
|
||||
}
|
||||
|
||||
/* DARK MODE */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--accent-color: #3b82f6;
|
||||
--accent-hover: #2563eb;
|
||||
--accent-light: #1e3a8a;
|
||||
--accent-border: #1e40af;
|
||||
|
||||
--text-primary: #f1f5f9;
|
||||
--text-secondary: #cbd5e1;
|
||||
--text-tertiary: #94a3b8;
|
||||
|
||||
--surface: #0f172a;
|
||||
|
||||
--header-bg: rgba(15, 23, 42, 0.85);
|
||||
--header-border: #334155;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Manual dark mode toggle */
|
||||
body.dark-mode {
|
||||
--accent-color: #3b82f6;
|
||||
--accent-hover: #2563eb;
|
||||
--accent-light: #1e3a8a;
|
||||
--accent-border: #1e40af;
|
||||
|
||||
--text-primary: #f1f5f9;
|
||||
--text-secondary: #cbd5e1;
|
||||
--text-tertiary: #94a3b8;
|
||||
|
||||
--surface: #0f172a;
|
||||
|
||||
--header-bg: rgba(15, 23, 42, 0.85);
|
||||
--header-border: #334155;
|
||||
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Smooth transition for dark mode */
|
||||
body {
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -42,6 +89,7 @@
|
||||
body {
|
||||
font-family: 'Normal', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
color: var(--text-primary);
|
||||
background-color: var(--surface);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@@ -49,7 +97,7 @@ button {
|
||||
padding: 10px 24px;
|
||||
border-radius: var(--radius-lg);
|
||||
border: 1px solid var(--header-border);
|
||||
background-color: var(--bg-surface);
|
||||
background-color: var(--surface);
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
85
console/src/static/theme-toggle.js
Normal file
85
console/src/static/theme-toggle.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Dark Mode Theme Toggle
|
||||
* Manages light/dark theme with localStorage persistence
|
||||
*/
|
||||
|
||||
const THEME_STORAGE_KEY = 'meb-console-theme';
|
||||
const DARK_MODE_CLASS = 'dark-mode';
|
||||
|
||||
/**
|
||||
* Initialize theme on page load
|
||||
*/
|
||||
function initializeTheme() {
|
||||
const savedTheme = localStorage.getItem(THEME_STORAGE_KEY);
|
||||
const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
// Use saved preference, or fallback to system preference
|
||||
const shouldBeDark = savedTheme ? savedTheme === 'dark' : prefersDarkMode;
|
||||
|
||||
if (shouldBeDark) {
|
||||
enableDarkMode();
|
||||
} else {
|
||||
disableDarkMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable dark mode
|
||||
*/
|
||||
function enableDarkMode() {
|
||||
document.documentElement.classList.add(DARK_MODE_CLASS);
|
||||
document.body.classList.add(DARK_MODE_CLASS);
|
||||
localStorage.setItem(THEME_STORAGE_KEY, 'dark');
|
||||
updateThemeToggleButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable dark mode (light mode)
|
||||
*/
|
||||
function disableDarkMode() {
|
||||
document.documentElement.classList.remove(DARK_MODE_CLASS);
|
||||
document.body.classList.remove(DARK_MODE_CLASS);
|
||||
localStorage.setItem(THEME_STORAGE_KEY, 'light');
|
||||
updateThemeToggleButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle dark mode
|
||||
*/
|
||||
function toggleDarkMode() {
|
||||
const isDarkMode = document.body.classList.contains(DARK_MODE_CLASS);
|
||||
|
||||
if (isDarkMode) {
|
||||
disableDarkMode();
|
||||
} else {
|
||||
enableDarkMode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update theme toggle button appearance
|
||||
*/
|
||||
function updateThemeToggleButton() {
|
||||
const themeBtn = document.getElementById('theme-toggle-btn');
|
||||
if (themeBtn) {
|
||||
const isDarkMode = document.body.classList.contains(DARK_MODE_CLASS);
|
||||
themeBtn.textContent = isDarkMode ? '☀️' : '🌙';
|
||||
themeBtn.title = isDarkMode ? 'Light Mode' : 'Dark Mode';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for system theme preference changes
|
||||
*/
|
||||
function listenToSystemThemeChanges() {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||
if (!localStorage.getItem(THEME_STORAGE_KEY)) {
|
||||
// If no manual preference set, follow system preference
|
||||
e.matches ? enableDarkMode() : disableDarkMode();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize on load
|
||||
document.addEventListener('DOMContentLoaded', initializeTheme);
|
||||
listenToSystemThemeChanges();
|
||||
Reference in New Issue
Block a user