暗色模式已经成了现代网页和应用的标配功能。很多人第一反应是用 JavaScript 来切换主题,但其实——只用 CSS 就能实现一个相当优雅的暗色模式,而且性能更好、代码更少。
核心武器:CSS 自定义属性
CSS 自定义属性(也叫 CSS 变量)是整个方案的基石。我们把所有颜色集中定义在 :root 里,主题切换时只需要改变变量的值。
:root {
--bg: #ffffff;
--text: #1e293b;
--card: #f8fafc;
--border: #e2e8f0;
--primary: #2563eb;
}
[data-theme="dark"] {
--bg: #0f172a;
--text: #e2e8f0;
--card: #1e293b;
--border: #334155;
--primary: #60a5fa;
}
这种集中管理的方式让主题切换变得极其干净——只需在一个地方定义颜色映射,整个页面的所有元素都会自动响应。
自动跟随系统偏好
使用 prefers-color-scheme 媒体查询,CSS 可以自动检测用户的系统主题设置:
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #0f172a;
--text: #e2e8f0;
--card: #1e293b;
--border: #334155;
--primary: #60a5fa;
}
}
:not([data-theme="light"]) 这个选择器很关键——它确保如果用户手动选了亮色模式,系统偏好不会覆盖用户的选择。
平滑过渡
直接切换颜色会显得很突兀。加一个简单的过渡就能让体验提升一个档次:
body {
background: var(--bg);
color: var(--text);
transition: background 0.3s ease, color 0.3s ease;
}
🎨 过渡效果的细节:
只对 background 和 color 做过渡,不要用
只对 background 和 color 做过渡,不要用
transition: all——会让边框、阴影等也跟着动画,显得拖泥带水。
切换按钮
JS 只需要做一件事:切换 data-theme 属性,并记住用户偏好。
const btn = document.querySelector('.theme-toggle');
const html = document.documentElement;
btn.addEventListener('click', () => {
const current = html.dataset.theme;
const next = current === 'dark' ? 'light' : 'dark';
html.dataset.theme = next;
localStorage.setItem('theme', next);
});
// 读取保存的偏好
const saved = localStorage.getItem('theme');
if (saved) html.dataset.theme = saved;
需要处理的细节
暗色模式下有几个容易忽略的地方:
- 图片和 Logo:透明背景的图片在暗色模式下可能看不清,可以用 CSS filter 微调
- 表单输入框:浏览器默认的 input 样式在暗色背景下很难看,需要手动覆盖
- 阴影:暗色模式下 box-shadow 要调得更暗、更分散,不然会显得很脏
- 代码块:代码高亮配色方案需要单独适配暗色模式
完整方案的优先级
最终的主题优先级应该是:用户手动选择 > 系统偏好 > 默认值。这个顺序保证了用户始终拥有最终控制权。
好的暗色模式不是把白底变黑底那么简单,而是对整个视觉层次的一次重新设计。