✨ Features: - Complete light theme with improved color palette - Theme switcher in header with sun/moon icons - Theme persistence using localStorage - Smooth transitions between themes 🎨 Theme Improvements: - Softer, easier-on-eyes light theme colors - Fixed text contrast issues across all components - Enhanced member card and tab text readability - Fixed hover effects that made text disappear - Improved member overlay header and body styling 🔧 Technical Implementation: - CSS variables system for easy theme management - JavaScript ThemeManager class for theme switching - Responsive design for mobile devices - Comprehensive hover state fixes - Z-index solutions for text visibility 📱 Components Fixed: - Member cards (hostname, IP, latency, details) - Navigation tabs and content - Task summaries and progress items - Capability forms and API endpoints - Member overlay popup - Form inputs and dropdowns - Status indicators and label chips 🎯 Accessibility: - WCAG AA contrast compliance - Proper color hierarchy - Clear visual feedback - Mobile-responsive design Files added: - public/styles/theme.css - Theme system and variables - public/scripts/theme-manager.js - Theme management logic - THEME_README.md - Comprehensive documentation - THEME_IMPROVEMENTS.md - Improvement details Files modified: - public/index.html - Added theme switcher and script includes - public/styles/main.css - Updated to use CSS variables
121 lines
3.6 KiB
JavaScript
121 lines
3.6 KiB
JavaScript
// Theme Manager - Handles theme switching and persistence
|
|
|
|
class ThemeManager {
|
|
constructor() {
|
|
this.currentTheme = this.getStoredTheme() || 'dark';
|
|
this.themeToggle = document.getElementById('theme-toggle');
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// Apply stored theme on page load
|
|
this.applyTheme(this.currentTheme);
|
|
|
|
// Set up event listener for theme toggle
|
|
if (this.themeToggle) {
|
|
this.themeToggle.addEventListener('click', () => this.toggleTheme());
|
|
}
|
|
|
|
// Listen for system theme changes
|
|
if (window.matchMedia) {
|
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
mediaQuery.addListener((e) => {
|
|
if (this.getStoredTheme() === 'system') {
|
|
this.applyTheme(e.matches ? 'dark' : 'light');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
getStoredTheme() {
|
|
try {
|
|
return localStorage.getItem('spore-ui-theme');
|
|
} catch (e) {
|
|
console.warn('Could not access localStorage for theme preference');
|
|
return 'dark';
|
|
}
|
|
}
|
|
|
|
setStoredTheme(theme) {
|
|
try {
|
|
localStorage.setItem('spore-ui-theme', theme);
|
|
} catch (e) {
|
|
console.warn('Could not save theme preference to localStorage');
|
|
}
|
|
}
|
|
|
|
applyTheme(theme) {
|
|
// Update data attribute on html element
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
|
|
// Update theme toggle icon
|
|
this.updateThemeIcon(theme);
|
|
|
|
// Store the theme preference
|
|
this.setStoredTheme(theme);
|
|
|
|
this.currentTheme = theme;
|
|
|
|
// Dispatch custom event for other components
|
|
window.dispatchEvent(new CustomEvent('themeChanged', {
|
|
detail: { theme: theme }
|
|
}));
|
|
}
|
|
|
|
updateThemeIcon(theme) {
|
|
if (!this.themeToggle) return;
|
|
|
|
const svg = this.themeToggle.querySelector('svg');
|
|
if (!svg) return;
|
|
|
|
// Update the SVG content based on theme
|
|
if (theme === 'light') {
|
|
// Sun icon for light theme
|
|
svg.innerHTML = `
|
|
<circle cx="12" cy="12" r="5"/>
|
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/>
|
|
`;
|
|
} else {
|
|
// Moon icon for dark theme
|
|
svg.innerHTML = `
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
|
`;
|
|
}
|
|
}
|
|
|
|
toggleTheme() {
|
|
const newTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
|
|
this.applyTheme(newTheme);
|
|
|
|
// Add a subtle animation to the toggle button
|
|
if (this.themeToggle) {
|
|
this.themeToggle.style.transform = 'scale(0.9)';
|
|
setTimeout(() => {
|
|
this.themeToggle.style.transform = 'scale(1)';
|
|
}, 150);
|
|
}
|
|
}
|
|
|
|
// Method to get current theme (useful for other components)
|
|
getCurrentTheme() {
|
|
return this.currentTheme;
|
|
}
|
|
|
|
// Method to set theme programmatically
|
|
setTheme(theme) {
|
|
if (['dark', 'light'].includes(theme)) {
|
|
this.applyTheme(theme);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize theme manager when DOM is loaded
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
window.themeManager = new ThemeManager();
|
|
});
|
|
|
|
// Export for use in other modules
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = ThemeManager;
|
|
}
|