Files
nexus/wiki/concepts/Theme-Toggle.md
2026-05-03 05:42:12 +08:00

3.2 KiB
Raw Blame History

title, type, tags, sources, last_updated
title type tags sources last_updated
Theme Toggle concept
css
javascript
ux
theme
dark-mode
design-ux-architect.md
2026-04-24

Definition

Theme Toggle 是允许用户在 light、dark 和 system 三种主题模式之间切换的 UI 组件。所有新站点必须将此组件作为默认必备功能,基于 localStorage 持久化和 prefers-color-scheme 检测实现。

HTML Structure

<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

.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

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();
});

Sources