#背景
暗色模式常见两种做法:写两套 CSS(.dark .card { ... }),或者用 CSS-in-JS 在运行时算颜色。前者随着组件变多会失控——同一个灰色出现在十几个文件里;后者需要构建链与运行时开销。
官网这类站点有更简单的解法:把颜色抽成令牌,一处在根上切换。零构建,纯原生。
#做法
#1. 令牌分三层
:root {
/* 基础色板(只在这里出现真实色值) */
--x-bg: #f7f8fb;
--x-fg: #0b1220;
--x-accent: #2563eb; /* 科技蓝:白字压上去 5.2:1 */
/* 派生态(由基础色板推导,组件只用这一层) */
--x-accent-soft: rgba(37, 99, 235, 0.1);
--x-line: #e4e8f0;
}
[data-theme='dark'] {
--x-bg: #05070d;
--x-fg: #eef3fb;
--x-accent: #60a5fa; /* 暗色下提亮,浅蓝底配深墨字 */
}组件里**只允许出现 var(--x-*)**,不允许写 #2563eb。这条规则靠搜索就能守住:grep -rn "#[0-9a-f]\{6\}" public/assets/web/css/ 的结果里,只应剩 :root 与 [data-theme] 两处。
#2. 切换只有一个属性
document.documentElement.dataset.theme = 'dark'配 color-scheme: dark 让浏览器原生控件(滚动条、表单)跟着变——少了这一步,暗色下会留一排刺眼的白色滚动条。
首次绘制要避免闪白:在 <head> 里同步读一次 localStorage 并写属性,不能等 DOMContentLoaded。
#3. 对比度必须量出来,不能凭感觉
配色最坑的地方是渐变按钮上的文字:浅蓝渐变 + 白字,对比度只有 2:1 左右,看着"很亮"其实读不清。做法是给按钮用实色底 + 深/浅字,并写个脚本量一遍:
php vabora ui:contrast # 明暗两套主题下逐项检查 WCAG AA检查会覆盖正文、次要文字、按钮、标签、代码块等所有"文字压底色"的组合,低于 4.5:1(大字号 3:1)就报红。这个命令比"设计稿看着还行"可靠得多。
#4. 少量内联样式的取舍
品牌色渐变文字用 background-clip: text,渐变两端都要压得住底色:
.x-hero__title em {
background: linear-gradient(104deg, var(--x-accent-deep), var(--x-accent));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}关键是两个端点都取深色档(--x-accent-deep → --x-accent),否则渐变末端会掉到 3:1 以下。
#结果
- 明暗切换只改一个属性,无闪烁、无重排;
- 全站 CSS 两个文件(约 76KB,gzip 18KB),没有构建步骤,改完刷新即刻生效;
- 对比度由
ui:contrast把关,改配色时不会悄悄引入不可读的组合。
#取舍
- 不用 Sass/Tailwind:站点规模(几十个组件)用原生变量足够;引入构建链的收益是"少打字",成本是"多一层要维护的工具"。规模上去后可以再换,令牌层能原样迁移。
- 不追动态取色:从封面图提取主色做主题很酷,但会让同一篇文章在两次访问里长得不一样,也难保证对比度。品牌色固定反而更稳。
评论
还没有评论,来说点什么吧。