用 CSS 变量搭一套会呼吸的配色系统

右上角那个深浅色开关,背后只有十几行 CSS 变量。 把颜色集中管理之后,改主题这件事从「翻遍样式表」变成了「改一处」。

9 分钟阅读

为什么先定义变量,再写样式

新手写样式常常是「遇到一处写一处颜色」:这里 #fafafa, 那里 #f9f9f9,半年后已经分不清哪个是正式的背景色。 变量做的事很简单 —— 给每个颜色一个名字, 让「用途」而不是「色号」出现在代码里。

好处是立刻能感受到的:想调整主色调,只改一行;想支持深色模式, 只写一套覆盖规则,而不必复制整份样式表。

第一步:给颜色起有意义的名字

名字应该描述用途,而不是外观。比如不要叫「浅灰」,而叫「次要文字」:

:root {
  /* 背景与表面 */
  --bg:          #faf8f5;   /* 页面底色 */
  --surface:     #ffffff;   /* 卡片、面板 */
  --border:      #e7e1d8;   /* 分隔线 */

  /* 文字层级 */
  --text:        #1b1917;   /* 正文 */
  --text-soft:   #55504a;   /* 摘要、说明 */
  --text-mute:   #8b8379;   /* 日期、标注 */

  /* 强调色 */
  --accent:      #c2410c;
  --accent-soft: #fdeee5;
}

第二步:用一套覆盖规则实现深色模式

深色模式不是把颜色反转,而是重新配一遍。同一组变量名, 换一组值,整个网站的外观就跟着变了:

:root[data-theme="dark"] {
  --bg:          #131211;
  --surface:     #1c1a18;
  --border:      #2e2a26;
  --text:        #eceae6;
  --text-soft:   #b5aea6;
  --accent:      #f97316;
}

/* 尊重系统设置:用户没手动选过时,跟随系统 */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* 同样的一组深色值 */
  }
}
两个细节很重要:一是深色下主色调要提亮, 深橙压在深底上会糊在一起;二是别忘加 color-scheme: dark,它能让滚动条、表单控件一起切换配色。

第三步:切换按钮其实只有几行 JS

var root = document.documentElement;
var saved = localStorage.getItem('site-theme');
if (saved) root.setAttribute('data-theme', saved);

// 点击时:算出下一个主题、应用、记住
document.querySelector('[data-theme-toggle]')
  .addEventListener('click', function () {
    var next = root.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
    root.setAttribute('data-theme', next);
    localStorage.setItem('site-theme', next);
  });

这里有个容易忽略的坑:如果等页面渲染完再读 localStorage, 深色用户会先看到一闪而过的白屏。解决办法是把这几行放进 <head> 里同步执行,在首屏绘制之前就把属性写好。

顺手统一其他「感觉」

变量不只管颜色。间距、圆角、阴影同样值得收进来 —— 当所有卡片用的是同一个 --radius, 整站的气质自然就统一了:

:root {
  --radius:    16px;
  --radius-sm: 10px;
  --shadow-sm: 0 1px 2px rgba(28, 25, 23, .06);
  --shadow-md: 0 6px 16px -6px rgba(28, 25, 23, .14);
  --ease:      cubic-bezier(.22, .61, .36, 1);

  /* 用一个变量统一控制所有过渡时长,方便整体调速 */
  --duration:  .28s;
}

.card { transition: transform var(--duration) var(--ease); }

最后一点心得

设计系统这个词听起来很重,落到个人项目上其实就是 把重复出现的东西起个名字。 一开始不必追求完备,用一处就抽一处; 抽到第十个变量的时候你会发现,改版这件事忽然变得很轻。

想看看实际效果?点一下右上角的太阳/月亮图标, 整站的每个颜色都会跟着一起呼吸。