CSS 变量 var 实现主题切换

FreeGuideOnline 最新 2026-07-06

切换暗黑模式


```javascript
const toggleBtn = document.getElementById('theme-toggle');
const htmlElement = document.documentElement;

// 检查本地存储,保持用户偏好
const currentTheme = localStorage.getItem('theme');
if (currentTheme === 'dark') {
  htmlElement.setAttribute('data-theme', 'dark');
}

toggleBtn.addEventListener('click', () => {
  if (htmlElement.hasAttribute('data-theme')) {
    htmlElement.removeAttribute('data-theme');
    localStorage.setItem('theme', 'light');
  } else {
    htmlElement.setAttribute('data-theme', 'dark');
    localStorage.setItem('theme', 'dark');
  }
});

这样,点击按钮会全局切换主题,所有使用 var() 的地方都会立即更新。

进阶技巧一:跟随系统主题自动切换

使用 prefers-color-scheme 媒体查询,无需 JS 即可让页面跟随操作系统主题。

@media (prefers-color-scheme: dark) {
  :root {
    --bg-body: #0f172a;
    --bg-card: #1e293b;
    --text-primary: #f1f5f9;
    /* 其他暗色变量 */
  }
}

如果同时希望保留手动切换能力,可以在 JS 中监听系统变化,并结合用户选择。更稳健的实践是检测用户是否已明确选择,若未选择则采用系统偏好。

进阶技巧二:主题变量继承与作用域

CSS 变量遵循层叠规则。你可以在任意元素上重新定义变量,仅在局部生效。例如,为特定组件设置独立主题:

.special-card {
  --bg-card: #fef3c7;
  --text-primary: #92400e;
}

这样 .special-card 内部使用的 var(--bg-card) 将取自局部定义,而不影响全局变量。

进阶技巧三:使用 HSL 和透明度变量

为提高灵活性,可以将颜色定义为 HSL 值,并单独控制透明度。例如:

:root {
  --color-primary-h: 221;
  --color-primary-s: 83%;
  --color-primary-l: 53%;
  --color-primary: hsl(var(--color-primary-h), var(--color-primary-s), var(--color-primary-l));
  --color-primary-alpha: hsla(var(--color-primary-h), var(--color-primary-s), var(--color-primary-l), 0.2);
}

[data-theme="dark"] {
  --color-primary-l: 65%; /* 仅调整亮度 */
}

这样在不同主题下只需调整 HSL 分量,仍能保持色相与饱和度一致。

完整示例代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS 变量主题切换</title>
  <style>
    :root {
      --bg-body: #ffffff;
      --text-primary: #111827;
      --bg-card: #f3f4f6;
      --border-color: #e5e7eb;
    }

    [data-theme="dark"] {
      --bg-body: #111827;
      --text-primary: #f9fafb;
      --bg-card: #1f2937;
      --border-color: #374151;
    }

    body {
      background-color: var(--bg-body);
      color: var(--text-primary);
      font-family: system-ui;
      display: flex;
      flex-direction: column;
      align-items: center;
      padding-top: 2rem;
      transition: background-color 0.3s, color 0.3s;
    }

    .card {
      background-color: var(--bg-card);
      border: 1px solid var(--border-color);
      padding: 1.5rem;
      border-radius: 0.5rem;
      width: 280px;
      margin-top: 1rem;
      transition: background-color 0.3s;
    }

    button {
      margin-top: 1.5rem;
      padding: 0.6rem 1.2rem;
      border: none;
      border-radius: 0.375rem;
      background-color: #3b82f6;
      color: white;
      cursor: pointer;
      font-size: 1rem;
    }

    button:hover {
      background-color: #2563eb;
    }
  </style>
</head>
<body>
  <h1>CSS 变量主题切换演示</h1>
  <div class="card">
    <p>这是一个卡片组件。</p>
    <p>背景与文字会随主题变化。</p>
  </div>
  <button id="theme-toggle">切换主题</button>
  <script>
    const btn = document.getElementById('theme-toggle');
    const html = document.documentElement;

    const saved = localStorage.getItem('theme');
    if (saved === 'dark') html.setAttribute('data-theme', 'dark');

    btn.addEventListener('click', () => {
      if (html.hasAttribute('data-theme')) {
        html.removeAttribute('data-theme');
        localStorage.setItem('theme', 'light');
      } else {
        html.setAttribute('data-theme', 'dark');
        localStorage.setItem('theme', 'dark');
      }
    });
  </script>
</body>
</html>