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:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -19,3 +19,5 @@ Thumbs.db
|
|||||||
.eslintcache
|
.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>
|
<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 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">
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -37,6 +48,7 @@
|
|||||||
<p id="cardCount">0 cards</p>
|
<p id="cardCount">0 cards</p>
|
||||||
|
|
||||||
<button id="editBtn">Edit</button>
|
<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="addCardBtn" title="Aggiungi Widget">+</button>
|
||||||
<button id="addMapBtn" title="Aggiungi Mappa">Map</button>
|
<button id="addMapBtn" title="Aggiungi Mappa">Map</button>
|
||||||
|
|
||||||
@@ -555,4 +567,15 @@ ws.onclose = () => {
|
|||||||
|
|
||||||
</script>
|
</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>
|
</html>
|
||||||
|
|||||||
@@ -11,6 +11,16 @@
|
|||||||
.expanded-chart-container { display: none; }
|
.expanded-chart-container { display: none; }
|
||||||
.comparison-sidebar { display: none; }
|
.comparison-sidebar { display: none; }
|
||||||
</style>
|
</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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -50,6 +60,7 @@
|
|||||||
<p class="last-update" id="lastUpdateText">In attesa di dati...</p>
|
<p class="last-update" id="lastUpdateText">In attesa di dati...</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="profile">
|
<div class="profile">
|
||||||
|
<button id="theme-toggle-btn" title="Dark Mode" style="padding: 8px 12px; font-size: 18px;">🌙</button>
|
||||||
<p id="sensorName">Sensore</p>
|
<p id="sensorName">Sensore</p>
|
||||||
<button id="changeSessionBtn">Cambia sessione</button>
|
<button id="changeSessionBtn">Cambia sessione</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -777,3 +788,14 @@ document.getElementById('saveSessionLabelBtn').onclick = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</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/style.css">
|
||||||
<link rel="stylesheet" href="/static/styles/rulesets.css">
|
<link rel="stylesheet" href="/static/styles/rulesets.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -24,6 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="rs-header-right">
|
<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>
|
<span class="rs-saving" id="savingIndicator">Salvato</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -594,5 +605,16 @@ function flash(text, elId = 'savingIndicator') {
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => loadRules());
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -244,6 +244,16 @@
|
|||||||
.map-bar .filter button { color: #cbd5e1; }
|
.map-bar .filter button { color: #cbd5e1; }
|
||||||
.map-bar .filter button.active { background: #3b82f6; color: #fff; }
|
.map-bar .filter button.active { background: #3b82f6; color: #fff; }
|
||||||
</style>
|
</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>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -279,6 +289,7 @@
|
|||||||
<h1>Sessioni</h1>
|
<h1>Sessioni</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<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="sessionNameDisplay"></span>
|
||||||
<span id="sessionMetaDisplay"></span>
|
<span id="sessionMetaDisplay"></span>
|
||||||
</div>
|
</div>
|
||||||
@@ -918,5 +929,16 @@ document.getElementById('changeSessionBtn').onclick = () => {
|
|||||||
// --- Init ---
|
// --- Init ---
|
||||||
document.addEventListener('DOMContentLoaded', loadSessionsList);
|
document.addEventListener('DOMContentLoaded', loadSessionsList);
|
||||||
</script>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -20,6 +20,53 @@
|
|||||||
--radius-lg: 12px;
|
--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;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -42,6 +89,7 @@
|
|||||||
body {
|
body {
|
||||||
font-family: 'Normal', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
font-family: 'Normal', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
background-color: var(--surface);
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +97,7 @@ button {
|
|||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
border: 1px solid var(--header-border);
|
border: 1px solid var(--header-border);
|
||||||
background-color: var(--bg-surface);
|
background-color: var(--surface);
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
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