06. 暗色模式

1. 什么是暗色模式

暗色模式(Dark Mode)是一种界面配色方案,使用深色背景和浅色文字,相比传统的亮色模式:

  • ✅ 减少眼睛疲劳(尤其在夜间)
  • ✅ 节省 OLED 屏幕电量
  • ✅ 提供视觉选择
  • ✅ 符合现代设计趋势
亮色模式              暗色模式
┌─────────────┐      ┌─────────────┐
│ 白色背景    │      │ 深灰背景    │
│ 黑色文字    │      │ 浅色文字    │
│ 浅色卡片    │      │ 深色卡片    │
└─────────────┘      └─────────────┘

2. Tailwind 暗色模式

2.1 启用暗色模式

Tailwind v4 默认支持暗色模式,无需额外配置。使用 dark: 前缀即可。

<!-- 亮色模式:白色背景 + 黑色文字 -->
<!-- 暗色模式:深灰背景 + 白色文字 -->
<div class="bg-white text-black dark:bg-gray-900 dark:text-white">
  内容
</div>

2.2 工作原理

Tailwind 通过 CSS 的 prefers-color-scheme 媒体查询或 .dark 类来切换:

/* 亮色模式 */
.bg-white { background-color: white; }

/* 暗色模式 - 根据系统偏好或父元素 .dark 类 */
@media (prefers-color-scheme: dark) {
  .dark\:bg-gray-900 { background-color: #111827; }
}

/* 或使用 .dark 类选择器 */
.dark .dark\:bg-gray-900 { background-color: #111827; }

3. 暗色模式策略

3.1 策略一:跟随系统(默认)

根据用户操作系统设置自动切换:

<!-- 系统是暗色模式时生效 -->
<div class="bg-white dark:bg-gray-900">
  跟随系统
</div>

特点:无需用户手动切换,自动适配系统偏好。

3.2 策略二:手动切换

通过 JavaScript 控制 .dark 类来切换:

<!-- HTML 结构 -->
<html class="dark">
  <body class="bg-white dark:bg-gray-900">
    ...
  </body>
</html>
// 切换暗色模式
function toggleDarkMode() {
  const html = document.documentElement;
  if (html.classList.contains('dark')) {
    html.classList.remove('dark');
    localStorage.setItem('theme', 'light');
  } else {
    html.classList.add('dark');
    localStorage.setItem('theme', 'dark');
  }
}

// 初始化时读取用户偏好
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
  document.documentElement.classList.add('dark');
} else if (savedTheme === 'light') {
  document.documentElement.classList.remove('dark');
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
  document.documentElement.classList.add('dark');
}

4. 暗色模式完整示例

4.1 基础页面

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.tailwindcss.com"></script>
  <title>暗色模式示例</title>
  <style>
    /* 确保暗色模式切换时背景平滑过渡 */
    body {
      transition: background-color 0.3s ease;
    }
  </style>
</head>
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">

<div class="container mx-auto px-4 py-8">
  <!-- 切换按钮 -->
  <div class="flex justify-end mb-8">
    <button 
      id="theme-toggle"
      class="px-4 py-2 bg-gray-200 dark:bg-gray-700 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition"
    >
      🌙 暗色模式
    </button>
  </div>
  
  <!-- 卡片组件 -->
  <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
    <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
      <h2 class="text-xl font-bold mb-2">卡片标题</h2>
      <p class="text-gray-600 dark:text-gray-400">
        这是一段描述文字,在暗色模式下会自动调整颜色。
      </p>
    </div>
    
    <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
      <h2 class="text-xl font-bold mb-2">另一个卡片</h2>
      <p class="text-gray-600 dark:text-gray-400">
        暗色模式下,背景变深,文字变浅。
      </p>
    </div>
  </div>
</div>

<script>
  const toggleBtn = document.getElementById('theme-toggle');
  const html = document.documentElement;
  
  // 初始化
  const savedTheme = localStorage.getItem('theme');
  if (savedTheme === 'dark') {
    html.classList.add('dark');
    toggleBtn.textContent = '☀️ 亮色模式';
  } else if (savedTheme === 'light') {
    html.classList.remove('dark');
    toggleBtn.textContent = '🌙 暗色模式';
  } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
    html.classList.add('dark');
    toggleBtn.textContent = '☀️ 亮色模式';
  }
  
  // 切换
  toggleBtn.addEventListener('click', () => {
    if (html.classList.contains('dark')) {
      html.classList.remove('dark');
      localStorage.setItem('theme', 'light');
      toggleBtn.textContent = '🌙 暗色模式';
    } else {
      html.classList.add('dark');
      localStorage.setItem('theme', 'dark');
      toggleBtn.textContent = '☀️ 亮色模式';
    }
  });
</script>

</body>
</html>

5. 暗色模式常用类

5.1 背景色

亮色模式 暗色模式 说明
bg-white dark:bg-gray-900 页面背景
bg-gray-100 dark:bg-gray-800 卡片/面板背景
bg-gray-200 dark:bg-gray-700 按钮/输入框背景

5.2 文字颜色

亮色模式 暗色模式 说明
text-gray-900 dark:text-gray-100 主要文字
text-gray-700 dark:text-gray-300 次要文字
text-gray-500 dark:text-gray-400 辅助文字

5.3 边框颜色

亮色模式 暗色模式 说明
border-gray-200 dark:border-gray-700 边框颜色
border-gray-300 dark:border-gray-600 分割线

5.4 阴影

<!-- 亮色模式有阴影,暗色模式阴影更淡 -->
<div class="shadow-md dark:shadow-gray-900/20">
  内容
</div>

6. 常见暗色模式模式

6.1 导航栏

<nav class="bg-white dark:bg-gray-900 shadow-md">
  <div class="container mx-auto px-4 py-3">
    <div class="flex justify-between items-center">
      <div class="text-xl font-bold text-gray-900 dark:text-white">Logo</div>
      <div class="space-x-4">
        <a href="#" class="text-gray-700 dark:text-gray-300 hover:text-blue-500">首页</a>
        <a href="#" class="text-gray-700 dark:text-gray-300 hover:text-blue-500">关于</a>
      </div>
    </div>
  </div>
</nav>

6.2 卡片

<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
  <h3 class="text-lg font-semibold text-gray-900 dark:text-white">卡片标题</h3>
  <p class="text-gray-600 dark:text-gray-400 mt-2">卡片描述文字</p>
  <button class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
    按钮
  </button>
</div>

6.3 表单输入

<input 
  type="text" 
  class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 
         bg-white dark:bg-gray-800 text-gray-900 dark:text-white 
         rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
  placeholder="输入内容"
>

6.4 下拉菜单

<select class="px-3 py-2 border border-gray-300 dark:border-gray-600 
               bg-white dark:bg-gray-800 text-gray-900 dark:text-white rounded">
  <option>选项1</option>
  <option>选项2</option>
</select>

7. 暗色模式颜色映射

7.1 推荐配色方案

元素 亮色模式 暗色模式
页面背景 bg-gray-50 dark:bg-gray-950
卡片背景 bg-white dark:bg-gray-900
主要文字 text-gray-900 dark:text-gray-50
次要文字 text-gray-600 dark:text-gray-400
边框 border-gray-200 dark:border-gray-800
链接 text-blue-600 dark:text-blue-400

7.2 颜色强度对应

亮色模式:数值越小颜色越浅
- 50: 最浅(几乎白色)
- 100-300: 浅色
- 400-600: 中等
- 700-900: 深色
- 950: 最深(几乎黑色)

暗色模式:数值越小颜色越深
- 50: 最深
- 100-300: 深色
- 400-600: 中等
- 700-900: 浅色
- 950: 最浅

8. 自定义暗色模式样式

8.1 配置暗色模式策略

tailwind.config.js 中:

module.exports = {
  darkMode: 'class',  // 使用 .dark 类切换
  // 或 darkMode: 'media'  // 跟随系统
}

8.2 自定义暗色模式颜色

module.exports = {
  theme: {
    extend: {
      colors: {
        dark: {
          bg: '#0f0f0f',
          surface: '#1a1a1a',
          border: '#2a2a2a',
        }
      }
    }
  }
}
<div class="bg-white dark:bg-dark-bg">
  自定义暗色模式背景
</div>

9. 避免暗色模式常见问题

9.1 图片处理

<!-- 图片在暗色模式下可能需要调整亮度 -->
<img src="logo.png" class="dark:brightness-90">

9.2 阴影处理

<!-- 暗色模式下阴影应该更淡 -->
<div class="shadow-md dark:shadow-gray-900/20">

9.3 滚动条

/* 自定义滚动条暗色模式 */
.dark ::-webkit-scrollbar-track {
  background: #1f2937;
}
.dark ::-webkit-scrollbar-thumb {
  background: #4b5563;
}

10. 练习

练习 1:暗色模式卡片

实现一个支持暗色模式的用户卡片:

  • 亮色:白色背景、深色文字
  • 暗色:深灰背景、浅色文字
  • 包含头像、姓名、职位、按钮

参考答案

<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 max-w-sm">
  <div class="flex items-center gap-4 mb-4">
    <div class="w-12 h-12 bg-blue-500 rounded-full"></div>
    <div>
      <h3 class="font-bold text-gray-900 dark:text-white">张三</h3>
      <p class="text-gray-600 dark:text-gray-400">前端工程师</p>
    </div>
  </div>
  <button class="w-full py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
    联系
  </button>
</div>

练习 2:暗色模式切换按钮

实现一个切换按钮,显示当前模式图标

参考答案

<button id="theme-toggle" class="p-2 rounded-lg bg-gray-200 dark:bg-gray-700">
  <span class="dark:hidden">🌙</span>
  <span class="hidden dark:inline">☀️</span>
</button>

11. 总结

概念 说明
dark: 前缀 暗色模式下生效的样式
跟随系统 默认,使用 prefers-color-scheme
手动切换 使用 .dark 类控制
存储偏好 localStorage 保存用户选择
颜色映射 亮色用浅色,暗色用深色

一句话总结

暗色模式用 dark: 前缀定义深色样式,通过系统偏好或 .dark 类自动切换。


Tailwind 里重复写 dark: 确实挺烦的,有几种方法可以避免。


方法一:使用 CSS 变量 + 主题切换(推荐)

在 CSS 中定义变量,Tailwind 类名只写一次:

/* style.css */
:root {
  --bg: #ffffff;
  --text: #1a1a1a;
}

.dark {
  --bg: #1a1a1a;
  --text: #f5f5f5;
}
<div class="bg-[var(--bg)] text-[var(--text)]">
  内容
</div>

方法二:自定义 Tailwind 插件

创建 dark-utils.js

// tailwind.config.js
module.exports = {
  plugins: [
    function({ addUtilities }) {
      addUtilities({
        '.card': {
          backgroundColor: 'white',
          color: 'black',
          '@media (prefers-color-scheme: dark)': {
            backgroundColor: '#1f2937',
            color: '#f3f4f6',
          },
        },
      })
    },
  ],
}

使用时只需一个类:<div class="card">


方法三:使用 PostCSS 嵌套

用 PostCSS 嵌套语法,把暗色样式写在一起:

.card {
  @apply bg-white text-gray-900;
  
  .dark & {
    @apply bg-gray-800 text-gray-100;
  }
}

需要安装 postcss-nested


方法四:CSS 变量 + Tailwind 任意值(最简洁)

/* style.css */
:root {
  --color-bg: theme('colors.white');
  --color-text: theme('colors.gray.900');
}

.dark {
  --color-bg: theme('colors.gray.800');
  --color-text: theme('colors.gray.100');
}
<div class="bg-[var(--color-bg)] text-[var(--color-text)]">
  内容
</div>

方法五:设计系统层面解决

定义语义化 CSS 变量,而不是直接用颜色:

:root {
  --surface-primary: #ffffff;
  --surface-secondary: #f5f5f5;
  --text-primary: #1a1a1a;
}

.dark {
  --surface-primary: #1e1e1e;
  --surface-secondary: #2d2d2d;
  --text-primary: #ffffff;
}

然后所有组件统一使用变量,颜色切换只改变量,组件代码完全不用动。


我的建议

如果项目规模不大,方法四最实用;如果要长期维护,方法五是更干净的设计系统方案。

你倾向于哪种?我可以帮你配置好。

12. 下一步

掌握了暗色模式后,接下来学习如何自定义主题:

👉 下一节07. 自定义主题

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐