/** * Theme switching functionality for blog application. * * Handles theme persistence in localStorage and applies * the selected theme to the document root element. * Supports system preference detection and manual theme switching. */ (function() { 'use strict'; const STORAGE_KEY = 'blog-theme'; const THEME_ATTRIBUTE = 'data-theme'; const THEME_LIGHT = 'light'; const THEME_DARK = 'dark'; /** * Get the currently stored theme preference. * Falls back to system preference if no stored value. * * @returns {string} The theme name ('light' or 'dark') */ function getStoredTheme() { try { const stored = localStorage.getItem(STORAGE_KEY); if (stored === THEME_LIGHT || stored === THEME_DARK) { return stored; } } catch (e) { console.warn('Failed to access localStorage:', e); } return getSystemPreference(); } /** * Detect system color scheme preference. * * @returns {string} 'dark' if system prefers dark mode, 'light' otherwise */ function getSystemPreference() { if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return THEME_DARK; } return THEME_LIGHT; } /** * Apply the specified theme to the document. * Updates the data-theme attribute on the html element. * * @param {string} theme - The theme to apply ('light' or 'dark') */ function applyTheme(theme) { const html = document.documentElement; if (html) { html.setAttribute(THEME_ATTRIBUTE, theme); } } /** * Save the theme preference to localStorage. * * @param {string} theme - The theme to save ('light' or 'dark') */ function saveTheme(theme) { try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) { console.warn('Failed to save theme to localStorage:', e); } } /** * Set and apply the specified theme. * Updates both the DOM and localStorage. * * @param {string} theme - The theme to set ('light' or 'dark') */ function setTheme(theme) { if (theme !== THEME_LIGHT && theme !== THEME_DARK) { console.warn('Invalid theme:', theme); return; } applyTheme(theme); saveTheme(theme); updateThemeIcons(theme); } /** * Toggle between light and dark themes. */ function toggleTheme() { const currentTheme = document.documentElement.getAttribute(THEME_ATTRIBUTE); const newTheme = currentTheme === THEME_DARK ? THEME_LIGHT : THEME_DARK; setTheme(newTheme); } /** * Update theme toggle icons based on current theme. * Shows/hides sun/moon icons appropriately. * * @param {string} theme - The current theme */ function updateThemeIcons(theme) { const lightIcons = document.querySelectorAll('[data-testid="theme-light-icon"]'); const darkIcons = document.querySelectorAll('[data-testid="theme-dark-icon"]'); lightIcons.forEach(icon => { icon.style.display = theme === THEME_LIGHT ? 'none' : 'block'; }); darkIcons.forEach(icon => { icon.style.display = theme === THEME_DARK ? 'none' : 'block'; }); } /** * Initialize theme on page load. * Applies stored theme and sets up event listeners. */ function init() { const theme = getStoredTheme(); applyTheme(theme); document.addEventListener('DOMContentLoaded', function() { updateThemeIcons(theme); const toggleBtn = document.querySelector('[data-testid="theme-toggle"]'); if (toggleBtn) { toggleBtn.addEventListener('click', toggleTheme); } }); window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) { try { const hasUserPreference = localStorage.getItem(STORAGE_KEY); if (!hasUserPreference) { const newTheme = e.matches ? THEME_DARK : THEME_LIGHT; applyTheme(newTheme); updateThemeIcons(newTheme); } } catch (err) { console.warn('Failed to handle system theme change:', err); } }); } const BlogTheme = { setTheme: setTheme, toggleTheme: toggleTheme, getStoredTheme: getStoredTheme, getSystemPreference: getSystemPreference, THEME_LIGHT: THEME_LIGHT, THEME_DARK: THEME_DARK }; if (typeof window !== 'undefined') { window.BlogTheme = BlogTheme; } init(); })();