CSS系列(19)-- 主题切换详解

发布于:2024-12-18 ⋅ 阅读:(10) ⋅ 点赞:(0)

前端技术探索系列:CSS 主题切换详解 🎨

致读者:探索动态主题的魅力 👋

前端开发者们,

今天我们将深入探讨 CSS 主题切换,学习如何构建灵活的主题系统。

主题系统设计 🚀

CSS 变量定义

/* 定义主题变量 */
:root {
    /* 亮色主题 */
    --light-primary: #007bff;
    --light-secondary: #6c757d;
    --light-background: #ffffff;
    --light-text: #333333;
    
    /* 暗色主题 */
    --dark-primary: #4dabf7;
    --dark-secondary: #adb5bd;
    --dark-background: #1a1a1a;
    --dark-text: #ffffff;
    
    /* 默认使用亮色主题 */
    --color-primary: var(--light-primary);
    --color-secondary: var(--light-secondary);
    --color-background: var(--light-background);
    --color-text: var(--light-text);
}

/* 暗色主题类 */
.theme-dark {
    --color-primary: var(--dark-primary);
    --color-secondary: var(--dark-secondary);
    --color-background: var(--dark-background);
    --color-text: var(--dark-text);
}

组件样式

/* 使用主题变量 */
.button {
    background-color: var(--color-primary);
    color: var(--color-background);
    border: 2px solid var(--color-primary);
    padding: 0.5em 1em;
    transition: all 0.3s ease;
}

.card {
    background-color: var(--color-background);
    color: var(--color-text);
    border: 1px solid var(--color-secondary);
    padding: 1rem;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* 主题切换动画 */
.theme-transition {
    transition: background-color 0.3s ease,
                color 0.3s ease,
                border-color 0.3s ease;
}

主题管理系统 🎯

class ThemeManager {
    constructor(options = {}) {
        this.options = {
            defaultTheme: 'light',
            storageKey: 'app-theme',
            transitionDuration: 300,
            ...options
        };
        
        this.themes = new Map();
        this.init();
    }

    init() {
        this.registerDefaultThemes();
        this.loadSavedTheme();
        this.setupListeners();
    }

    registerDefaultThemes() {
        this.registerTheme('light', {
            primary: '#007bff',
            secondary: '#6c757d',
            background: '#ffffff',
            text: '#333333'
        });

        this.registerTheme('dark', {
            primary: '#4dabf7',
            secondary: '#adb5bd',
            background: '#1a1a1a',
            text: '#ffffff'
        });
    }

    registerTheme(name, colors) {
        this.themes.set(name, {
            name,
            colors,
            cssVars: this.generateCSSVars(colors)
        });
    }

    generateCSSVars(colors) {
        return Object.entries(colors).reduce((vars, [key, value]) => {
            vars[`--color-${key}`] = value;
            return vars;
        }, {});
    }

    loadSavedTheme() {
        const savedTheme = localStorage.getItem(this.options.storageKey);
        this.setTheme(savedTheme || this.options.defaultTheme);
    }

    setTheme(themeName) {
        const theme = this.themes.get(themeName);
        if (!theme) return;

        // 添加过渡动画
        document.documentElement.classList.add('theme-transition');

        // 应用主题变量
        Object.entries(theme.cssVars).forEach(([property, value]) => {
            document.documentElement.style.setProperty(property, value);
        });

        // 保存主题选择
        localStorage.setItem(this.options.storageKey, themeName);

        // 触发主题变更事件
        this.dispatchThemeChange(theme);

        // 移除过渡动画类
        setTimeout(() => {
            document.documentElement.classList.remove('theme-transition');
        }, this.options.transitionDuration);
    }

    setupListeners() {
        // 监听系统主题变化
        if (window.matchMedia) {
            window.matchMedia('(prefers-color-scheme: dark)')
                .addEventListener('change', e => {
                    this.setTheme(e.matches ? 'dark' : 'light');
                });
        }

        // 监听自定义主题变化事件
        document.addEventListener('themeChange', (e) => {
            if (e.detail.theme) {
                this.setTheme(e.detail.theme);
            }
        });
    }

    dispatchThemeChange(theme) {
        const event = new CustomEvent('themeChanged', {
            detail: { theme }
        });
        document.dispatchEvent(event);
    }

    getCurrentTheme() {
        return localStorage.getItem(this.options.storageKey) || 
               this.options.defaultTheme;
    }

    toggleTheme() {
        const currentTheme = this.getCurrentTheme();
        const newTheme = currentTheme === 'light' ? 'dark' : 'light';
        this.setTheme(newTheme);
    }

    createThemeSelector() {
        const selector = document.createElement('select');
        selector.className = 'theme-selector';

        this.themes.forEach((theme, name) => {
            const option = document.createElement('option');
            option.value = name;
            option.text = name.charAt(0).toUpperCase() + name.slice(1);
            selector.appendChild(option);
        });

        selector.value = this.getCurrentTheme();
        selector.addEventListener('change', (e) => {
            this.setTheme(e.target.value);
        });

        return selector;
    }
}

使用示例 💫

// 初始化主题管理器
const themeManager = new ThemeManager({
    defaultTheme: 'light',
    storageKey: 'my-app-theme'
});

// 注册自定义主题
themeManager.registerTheme('ocean', {
    primary: '#0077be',
    secondary: '#40a9ff',
    background: '#f0f8ff',
    text: '#333333'
});

// 创建主题切换按钮
const toggleButton = document.createElement('button');
toggleButton.textContent = '切换主题';
toggleButton.addEventListener('click', () => {
    themeManager.toggleTheme();
});

// 监听主题变化
document.addEventListener('themeChanged', (e) => {
    console.log('主题已切换到:', e.detail.theme.name);
});

最佳实践建议 💡

  1. 主题设计

    • 使用CSS变量
    • 颜色系统设计
    • 主题分层管理
    • 渐进增强
  2. 性能优化

    • 缓存主题设置
    • 优化切换动画
    • 按需加载主题
    • 减少重绘重排
  3. 用户体验

    • 平滑过渡
    • 记住用户选择
    • 响应系统主题
    • 提供预览功能
  4. 开发建议

    • 组件解耦
    • 主题复用
    • 易于扩展
    • 维护文档

写在最后 🌟

CSS 主题切换是提升用户体验的重要特性,通过合理的设计可以实现灵活且高效的主题系统。

进一步学习资源 📚

  • 主题设计指南
  • 色彩系统设计
  • 性能优化策略
  • 案例分析研究

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻