Open arvinxx opened 3 years ago
Ref: 实时主题切换方案研究 · 语雀
目前 css variables 和 less variable、less function 存在的问题:less function 视野里没有 css variable 的值
这就导致使用了 less function 的库,(例如 antd ),就没法用 css variable 来覆写 @primary-color
这样的变量,进而失去了利用 css variables 动态切换主题方案的能力。
社区相关讨论:
使用 less 变量很重要的一个点是它支持 function。而 css 变量不支持 function。上述很让人头疼的一点是 less 是一个编译时,编译时的视野里是没有 css variable 的。 但是我们希望能把 less 和 css variable 结合起来,结合 less 静态编译的能力和 css variable 动态切换的能力,进而提升样式的实现灵活性。 换个角度来思考,如果 less 无法直接支持 css variable,那么让 less 先行编译,然后在得到具体值时再挂载到css variable 上,再将 css variable 赋予给 less 变量,这样是不是就可以解决这个问题了? 即让 less 做两次解析。第一次计算值,第二次再赋予变量。
// color palettes
@blue-base: #1890ff;
@blue-1: color(~`colorPalette('@{blue-6}', 1) `);
@blue-2: color(~`colorPalette('@{blue-6}', 2) `);
@blue-3: color(~`colorPalette('@{blue-6}', 3) `);
@blue-4: color(~`colorPalette('@{blue-6}', 4) `);
@blue-5: color(~`colorPalette('@{blue-6}', 5) `);
@blue-6: @blue-base;
@blue-7: color(~`colorPalette('@{blue-6}', 7) `);
@blue-8: color(~`colorPalette('@{blue-6}', 8) `);
@blue-9: color(~`colorPalette('@{blue-6}', 9) `);
@blue-10: color(~`colorPalette('@{blue-6}', 10) `);
@import '~antd/es/style/themes/default';
@brand-color: #233ad2;
:root {
@blue-base: @brand-color;
--blue-1: @blue-1;
--blue-2: @blue-2;
--blue-3: @blue-3;
--blue-4: @blue-4;
--blue-5: @blue-5;
--blue-6: @blue-6;
--blue-7: @blue-7;
--blue-8: @blue-8;
--blue-9: @blue-9;
--blue-10: @blue-10;
}
@dark-mode-primary-color: mix(@brand-color, @component-background, 85%);
:root [theme^='dark'] {
// 需要在 dark 下导入 dark 相关的变量
// 这样就可以使得在不同的 theme scope 中
// 同样的 @blue-base 会得到不同的值
@import '~antd/es/style/themes/dark';
@blue-base: @dark-mode-primary-color;
--blue-1: @blue-1;
--blue-2: @blue-2;
--blue-3: @blue-3;
--blue-4: @blue-4;
--blue-5: @blue-5;
--blue-6: @blue-6;
--blue-7: @blue-7;
--blue-8: @blue-8;
--blue-9: @blue-9;
--blue-10: @blue-10;
}
这样的话,就有了
亮色模式 | 暗色模式 |
---|---|
最后这个阶段进行覆写,因此应该处于 less 编译周期的最后,在计算完值之后进行替换。因此需要找到 less 的编译声明周期方法,然后看是否能在相应的阶段注入替换。
@blue-base: var(--blue-base);
@blue-1: var(--blue-1);
@blue-2: var(--blue-2);
@blue-3: var(--blue-3);
@blue-4: var(--blue-4);
@blue-5: var(--blue-5);
@blue-6: var(--blue-6);
@blue-7: var(--blue-7);
@blue-8: var(--blue-8);
@blue-9: var(--blue-9);
@blue-10: var(--blue-10);
上述的思路已经需要渗透进 less 的编译流程,因此需要用插件的方式来解决这个问题。(在https://github.com/less/less.js/issues/3600 提了个相关 issue)
其中需要用到 less AST 的能力,目前挺难办的,正在 How to replace a Declaration node By an array of Declaration and a Ruleset when using visitor to modify AST ? · Issue #3601 · less/less.js 寻求作者帮助。
哇哦,我正在给一个现有 less 项目增加 theme 支持,调研的时候找到了这里。我想问一下参考对象中的 https://github.com/miyamarisubs/less-plugin-custom-properties 有什么问题以至不能满足你的需求吗?
@leeyeh 上述插件只是将所有 less 变量变成了 css 变量。例如
// multiplyTwo 是一个 less function,将传入的值×2
@base-number: 10;
@multiply-number: multiplyTwo(@base-number);
.use {
base: @base-number;
multiply: @multiply-number;
}
会变成
:root {
--base-number: 10;
--multiply-number: multiplyTwo(var(--base-number));
}
.use {
base: var(--base-number);
multiply: var(--multiply-number);
}
而进入less function 计算环节时,就会抛出var(--base-number) is invalid
错误。
原因就是 less function 无法使用 css 变量进行计算。相关 Issue 我在二楼 Why 里有讲。
这就导致像 antd 这样的组件库就无法使用那个插件,因为它的色板系统是通过 less function 生成的,同时已经定义的 less 变量中有不少使用了 darken、light 等 less 内置 function,都会在直接转移成 css variable 时报错。
感谢明确问题。其实我疑惑的点是,如果按照设想的方式,在 less 编译阶段就将所有的 theme 对应的 variables 展开,那 css variable 动态的特性就大打折扣了。而且在我现在的项目里有非常多中间变量的定义,虽然对比纯 less 的 theme 方案最终编译的 css 大小可能是减少了,但好像还是会随着主题数量的增加增加。
可能我还是会先尝试一下运行时 calc + hsl 的方式来替代 darken 这样的 function(我的需求里没有特别多其他复杂的 function),也期待这个项目的进展,如果方便我也可以帮忙测试。
那 css variable 动态的特性就大打折扣了。
不是的,这反而能够更好地利用 less 的局部作用域的能力,动态性会更好。还是同样一个 case,我加一个 local 的局部状态覆写:
@base-number: 10;
@multiply-number: multiplyTwo(@base-number);
[scope='local'] {
@base-number: 2;
}
.use {
base: @base-number;
multiply: @multiply-number;
}
这样在生成 css 时就可以去自动生成下面这样的结果:
:root {
--base-number: 10;
--multiply-number: 20;
}
:root[scope='local'] {
--base-number: 2;
--multiply-number: 4;
}
.use {
color: var(--base-number);
multiply: var(--multiply-number);
}
这样其实才是最大程度发挥 less 局部作用域 + css 变量动态性特点的方法。
我说的动态性指的是另一个角度,比如上面的例子里我就无法直接在客户端通过覆盖 --base-number 来看效果了(因为 multiply-number 被展开了),必须要通过重新编译才行。我的 use case 里有大量类似的中间变量,举个极端的例子:
另一方面就是本来在纯 less 的方案中这些中间变量是不会产生任何 CSS 的,但是把他们「编译」成 CSS variables 之后则会是实际的 CSS 代码,如果给每个主题都展开一份中间变量列表这个量就是我需要考虑的了。
上面的 case 对这个项目的价值可能在于,在不涉及到 function 的情况下,或许不展开会是更好的选择(或者是一个有用的选项)。
这东西最近有啥进度吗,😃
@text-color-secondary: var(--ant-ext-color-secondary); shade(@text-color-secondary, 40%) 转换为 --ant-ext-color-secondary-shade-40 变量, 有什么好的方案么?
动机
简单来说,就是让 less 变量可以无痛迁移到 css variable 去。详见二楼 ⬇️
功能特性
RoadMap
cosmiconfig
预期使用方法
1. 配置
使用 cosmiconfig 式配置。
准备暂且只支持一个
variables
参数,其他需求后面再看如此一来,插件就会识别到
primary-color
和green-4
变量是需要提升为 css variables 的。2. 引入插件
lessc 直接执行的话
引入到配置项中
3. 使用
如下声明
会生成
参考对象
社区插件仓库
开发笔记
资料库