Files
cv-site/static/js/color-theme.js
T
juanatsap f3cce51fb3 feat: implement color theme switcher with dynamic button colors
Complete color theme system (light/dark/auto) with dynamic UI:

Features:
- Color theme switcher with auto/light/dark modes
- Dynamic button colors on hover (purple/yellow/blue per theme)
- localStorage persistence across sessions
- Proper button positioning (desktop and mobile)
- Mobile: 5-button layout with theme before info button

Fixes:
- CSP updated to allow jsDelivr CDN for iconify icons
- Button repositioning: Download PDF and Print Friendly at top
- Hover-only colors (not persistent)
- Mobile button order corrected

Files:
- static/css/color-theme.css - Theme system with CSS variables
- static/js/color-theme.js - Theme switching logic
- templates/partials/color-theme-switcher.html - Button component
- internal/middleware/security.go - CSP fix for jsDelivr
- tests/mjs/13-color-theme-switcher.test.mjs - Comprehensive test
- tests/TEST-SUMMARY.md - Updated test documentation
2025-11-18 15:49:30 +00:00

98 lines
2.8 KiB
JavaScript

/**
* COLOR THEME SYSTEM
* Pure JavaScript implementation (replacing hyperscript due to parsing issues)
* Handles light/dark/auto theme switching
*/
// Set color theme
function setColorTheme(mode) {
// Save preference to localStorage
localStorage.setItem('color-theme-mode', mode);
// Apply theme to document
document.documentElement.setAttribute('data-color-theme', mode);
// Update button icon and color based on mode
const themeButton = document.querySelector('#color-theme-switcher');
const themeIcon = document.querySelector('#themeIcon');
if (themeButton) {
// Set data attribute for CSS styling
themeButton.setAttribute('data-theme-mode', mode);
}
if (themeIcon) {
if (mode === 'light') {
themeIcon.setAttribute('icon', 'mdi:white-balance-sunny');
} else if (mode === 'dark') {
themeIcon.setAttribute('icon', 'mdi:moon-waning-crescent');
} else {
themeIcon.setAttribute('icon', 'mdi:theme-light-dark');
}
}
// Update button active states (for hidden compatibility buttons)
const buttons = document.querySelectorAll('.theme-option-btn');
buttons.forEach(btn => {
if (btn.getAttribute('data-theme-mode') === mode) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
}
// Initialize color theme
function initColorTheme() {
// Get saved preference or default to 'auto'
const savedTheme = localStorage.getItem('color-theme-mode') || 'auto';
setColorTheme(savedTheme);
}
// Setup button click handler
function setupColorThemeButton() {
const button = document.querySelector('#color-theme-switcher');
if (button) {
button.addEventListener('click', () => {
// Get current theme
const currentTheme = localStorage.getItem('color-theme-mode') || 'auto';
// Cycle: auto → light → dark → auto
if (currentTheme === 'auto') {
setColorTheme('light');
} else if (currentTheme === 'light') {
setColorTheme('dark');
} else {
setColorTheme('auto');
}
});
}
}
// Watch system theme changes (optional enhancement)
function watchSystemTheme() {
const darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeQuery.addEventListener('change', () => {
const currentMode = localStorage.getItem('color-theme-mode');
if (currentMode === 'auto' || currentMode === null) {
// Theme will automatically update via CSS
// Just ensure the UI reflects the current state
setColorTheme('auto');
}
});
}
// Initialize on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
initColorTheme();
setupColorThemeButton();
watchSystemTheme();
});
} else {
initColorTheme();
setupColorThemeButton();
watchSystemTheme();
}