feat: add theme toggle functionality and update localization files

- Implemented a theme toggle feature allowing users to switch between light and dark modes.
- Added a new ThemeToggle component for the theme switching button.
- Updated theme CSS to support dark mode variables and system preference detection.
- Enhanced localization files to include translations for the "toggleTheme" key across multiple languages.
This commit is contained in:
Your Name 2026-01-21 15:00:34 +08:00
parent 1f850dd926
commit db5f47586e
72 changed files with 338 additions and 77 deletions

102
public/theme.js Normal file
View file

@ -0,0 +1,102 @@
// ===== Theme Toggle Script =====
// Handles dark/light mode switching with localStorage persistence
// and system preference detection
(function () {
const THEME_KEY = "theme";
// Get stored theme or detect system preference
function getPreferredTheme() {
const storedTheme = localStorage.getItem(THEME_KEY);
if (storedTheme) {
return storedTheme;
}
// Return null to let CSS handle system preference
return null;
}
// Get current effective theme (what's actually being displayed)
function getEffectiveTheme() {
const storedTheme = localStorage.getItem(THEME_KEY);
if (storedTheme) {
return storedTheme;
}
// Check system preference
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
// Apply theme to document
function applyTheme(theme) {
const root = document.documentElement;
if (theme === "dark") {
root.setAttribute("data-theme", "dark");
} else if (theme === "light") {
root.setAttribute("data-theme", "light");
} else {
// Remove attribute to follow system preference
root.removeAttribute("data-theme");
}
updateThemeIcons();
}
// Update theme toggle icons visibility
function updateThemeIcons() {
const effectiveTheme = getEffectiveTheme();
const lightIcon = document.getElementById("theme-icon-light");
const darkIcon = document.getElementById("theme-icon-dark");
if (lightIcon && darkIcon) {
if (effectiveTheme === "dark") {
// In dark mode, show sun icon (to switch to light)
lightIcon.classList.remove("hidden");
darkIcon.classList.add("hidden");
} else {
// In light mode, show moon icon (to switch to dark)
lightIcon.classList.add("hidden");
darkIcon.classList.remove("hidden");
}
}
}
// Toggle theme
function toggleTheme() {
const currentTheme = getEffectiveTheme();
const newTheme = currentTheme === "dark" ? "light" : "dark";
localStorage.setItem(THEME_KEY, newTheme);
applyTheme(newTheme);
}
// Initialize theme on page load
function initTheme() {
const preferredTheme = getPreferredTheme();
applyTheme(preferredTheme);
// Listen for system preference changes
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
// Only update if no manual preference is set
if (!localStorage.getItem(THEME_KEY)) {
applyTheme(null);
}
});
}
// Apply theme immediately to prevent flash
const preferredTheme = getPreferredTheme();
if (preferredTheme) {
document.documentElement.setAttribute("data-theme", preferredTheme);
}
// Set up event listener when DOM is ready
document.addEventListener("DOMContentLoaded", function () {
initTheme();
const themeToggle = document.getElementById("theme-toggle");
if (themeToggle) {
themeToggle.addEventListener("click", toggleTheme);
}
});
// Expose toggle function globally for programmatic access
window.toggleTheme = toggleTheme;
window.getEffectiveTheme = getEffectiveTheme;
})();