125 lines
3.2 KiB
Markdown
125 lines
3.2 KiB
Markdown
---
|
||
title: "Theme Toggle"
|
||
type: concept
|
||
tags: [css, javascript, ux, theme, dark-mode]
|
||
sources: [design-ux-architect.md]
|
||
last_updated: 2026-04-24
|
||
---
|
||
|
||
## Definition
|
||
|
||
Theme Toggle 是允许用户在 light、dark 和 system 三种主题模式之间切换的 UI 组件。所有新站点必须将此组件作为默认必备功能,基于 localStorage 持久化和 `prefers-color-scheme` 检测实现。
|
||
|
||
## HTML Structure
|
||
|
||
```html
|
||
<div class="theme-toggle" role="radiogroup" aria-label="Theme selection">
|
||
<button class="theme-toggle-option" data-theme="light" role="radio" aria-checked="false">
|
||
<span aria-hidden="true">☀️</span> Light
|
||
</button>
|
||
<button class="theme-toggle-option" data-theme="dark" role="radio" aria-checked="false">
|
||
<span aria-hidden="true">🌙</span> Dark
|
||
</button>
|
||
<button class="theme-toggle-option" data-theme="system" role="radio" aria-checked="true">
|
||
<span aria-hidden="true">💻</span> System
|
||
</button>
|
||
</div>
|
||
```
|
||
|
||
## CSS Styles
|
||
|
||
```css
|
||
.theme-toggle {
|
||
position: relative;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
background: var(--bg-secondary);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 24px;
|
||
padding: 4px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.theme-toggle-option {
|
||
padding: 8px 12px;
|
||
border-radius: 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: var(--text-secondary);
|
||
background: transparent;
|
||
border: none;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.theme-toggle-option.active {
|
||
background: var(--primary-500);
|
||
color: white;
|
||
}
|
||
```
|
||
|
||
## JavaScript Implementation
|
||
|
||
```javascript
|
||
class ThemeManager {
|
||
constructor() {
|
||
this.currentTheme = this.getStoredTheme() || this.getSystemTheme();
|
||
this.applyTheme(this.currentTheme);
|
||
this.initializeToggle();
|
||
}
|
||
|
||
getSystemTheme() {
|
||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||
}
|
||
|
||
getStoredTheme() {
|
||
return localStorage.getItem('theme');
|
||
}
|
||
|
||
applyTheme(theme) {
|
||
if (theme === 'system') {
|
||
document.documentElement.removeAttribute('data-theme');
|
||
localStorage.removeItem('theme');
|
||
} else {
|
||
document.documentElement.setAttribute('data-theme', theme);
|
||
localStorage.setItem('theme', theme);
|
||
}
|
||
this.currentTheme = theme;
|
||
this.updateToggleUI();
|
||
}
|
||
|
||
initializeToggle() {
|
||
const toggle = document.querySelector('.theme-toggle');
|
||
if (toggle) {
|
||
toggle.addEventListener('click', (e) => {
|
||
if (e.target.matches('.theme-toggle-option')) {
|
||
this.applyTheme(e.target.dataset.theme);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
updateToggleUI() {
|
||
const options = document.querySelectorAll('.theme-toggle-option');
|
||
options.forEach(option => {
|
||
option.classList.toggle('active', option.dataset.theme === this.currentTheme);
|
||
});
|
||
}
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
new ThemeManager();
|
||
});
|
||
```
|
||
|
||
## Related Concepts
|
||
|
||
- [[CSS-Design-System]]:提供颜色变量系统支撑主题切换
|
||
- [[Theme-Manager]]:JavaScript 主题状态管理类
|
||
- [[Responsive-Breakpoints]]:与其他响应式设计组件协同
|
||
- [[Layout-Framework]]:通常放置于 Header/Navigation 区域
|
||
|
||
## Sources
|
||
|
||
- [[design-ux-architect]] — Theme Toggle 的完整 HTML/CSS/JS 实现规范
|