Less 通过@media (prefers-color-scheme: dark)内重定义语义变量实现深色模式,所有颜色必须从变量读取且在明暗媒体查询中分别赋值,禁用darken()/lighten()等破坏对比度的函数,组件变量需显式重复声明或用mixin管理。

如何用 Less 变量控制深色模式开关
深色模式不是靠 JS 切换 class 就完事的,核心在于 CSS 层面的主题变量是否能被条件化注入。Less 本身不支持运行时主题切换,但可以通过 @media (prefers-color-scheme: dark) + 变量重定义实现静态编译时双主题输出。
关键点:所有颜色、背景、边框等视觉属性必须从变量读取,且深色变量需在暗色媒体查询内重新赋值。
- 不要直接写死
color: #333,必须用@text-color这类变量 - 深色变量不能写在顶层作用域,否则会覆盖浅色值;必须包裹在
@media (prefers-color-scheme: dark) { ... }内 - Less 编译后生成的是纯 CSS,不带逻辑分支——所以“切换”本质是浏览器根据系统偏好自动匹配对应规则
为什么 darken() 和 lighten() 在主题里容易翻车
这些函数看似方便,实则破坏主题一致性:它们基于当前色值做算术偏移,但浅色模式下的 #fff 调用 darken(@bg, 10%) 得到的是灰色,而深色模式下 #111 做同样操作可能变成纯黑,失去可读性。
更糟的是,contrast() 等函数在编译期就执行,无法感知最终渲染上下文,导致对比度失效。
立即学习“前端免费学习笔记(深入)”;
- 避免对基础色变量做动态计算,改用预设语义变量:
@surface-1、@surface-2、@text-primary - 如果必须复用逻辑,用 mixin 封装,但参数必须是明确色值,而非依赖当前变量状态
- 深色模式中慎用
fadeout()——半透明叠加在深底色上常导致灰蒙蒙,不如直接定义@shadow-sm的 rgba 值
如何让组件级变量响应主题(比如 Button 的 @btn-bg)
组件变量不能只定义一次,得在两个媒体查询里分别声明。Less 不支持“变量作用域继承”,所以必须显式重复赋值或借助 mixin。
推荐用 mixin 集中管理,避免遗漏:
.theme-vars() {
@btn-bg: #007bff;
@btn-text: #fff;
}
@media (prefers-color-scheme: light) {
.theme-vars();
}
@media (prefers-color-scheme: dark) {
.theme-vars();
@btn-bg: #2a3b5c;
@btn-text: #e6e6e6;
}
- 每个组件的变量都要在两个媒体查询里出现,漏掉一个就会回退到默认值(通常是浅色)
- 不要试图用
if()函数判断主题——它只在编译期求值,无法响应用户系统设置 - 变量名保持语义化,避免
@btn-bg-dark这种命名,它会让维护者误以为是独立变量
兼容旧浏览器时 @media (prefers-color-scheme) 怎么兜底
IE 和 Safari 12.1 以下不支持该媒体查询,此时所有深色规则会被忽略,页面默认按浅色渲染。这不是 bug,是预期行为。
若需强制支持手动切换(如页面右上角“?”按钮),就得引入 JS 控制 class,并用 Less 编写两套 class 前缀规则:
.theme-light .btn { background: @btn-bg; }
.theme-dark .btn { background: @btn-bg-dark; }
- 仅当项目明确要求手动切换时才加 JS 层,否则优先用系统原生方案——更轻量、无 FOUC、无障碍友好
- 不要混用两种方案(即同时监听
prefers-color-scheme和 class),会导致样式层叠混乱 - 构建时可通过
lessc --modify-var="@btn-bg=#2a3b5c"生成独立深色 CSS 文件,适合 SSR 场景


















