动态主题
动态主题
动态样式文件
改变皮肤 link 元素的 href 地址。例如:
<link id="skinLink" href="skin-default.css" rel="stylesheet" type="text/css">
换皮肤的时候 JS 改变 href 属性值:
skinLink.href = "skin-red.css";
webpack-theme-color-replacer
在 m-fe-rtw, 基于 React & TypeScript & Webpack 的微前端复杂应用项目模板中,我们使用了 webpack-theme-color-replacer。webpack-theme-color-replacer 在 Webpack 构建时,在 emit 事件(准备写入 dist 结果文件时)中,将即将生成的所有 CSS 文件的内容中 带有指定颜色的 CSS 规则单独提取出来,再合并为一个 theme-colors.css 输出文件。然后在切换主题色时,下载这个文件,并替换为需要的颜色,应用到页面上。这样,下载的样式中就只包含颜色相关的 CSS 规则,文件较小;同时它已经包含了项目中所有的 CSS 中的指定颜色样式,一次下载全部颜色样式都搞定。首先我们添加替换插件:
new ThemeColorReplacer({
fileName: "theme-colors-[contenthash:8].css",
matchColors: getAntdSerials("#5d4bff"), // 主色系列
// 改变样式选择器,解决样式覆盖问题
changeSelector(selector) {
switch (selector) {
case ".ant-calendar-today .ant-calendar-date":
return ":not(.ant-calendar-selected-date)" + selector;
case ".ant-btn:focus,.ant-btn:hover":
return ".ant-btn:focus:not(.ant-btn-primary),.ant-btn:hover:not(.ant-btn-primary)";
case ".ant-btn.active,.ant-btn:active":
return ".ant-btn.active:not(.ant-btn-primary),.ant-btn:active:not(.ant-btn-primary)";
default:
return selector;
}
},
});
该插件会生成一个 CSS 文件:
.el-tag {
color: #f67a17;
}
.el-tag.is-hit {
border-color: #f67a17;
}
并且在 Runtime 中插入如下代码:
window.__theme_COLOR_cfg = {
url: "theme-colors-05d50e3a.css",
colors: [
"#5d4bff",
// ...
],
};
这样就能获得到某个颜色对应的元素 ID,在执行颜色切换的时候,直接调用 Client 的 client.changer.changeColor(options, Promise);
changeColor 方法:
// 首先判断 theme-colors.css 是否已经被插入到了文档,如果未插入则执行拉取然后再插入
setCssTo(elStyle, elStyle.innerText);
// 执行 Style 内置替换
function setCssTo(elStyle, cssText) {
cssText = _this.replaceCssText(cssText, oldColors, newColors);
elStyle.color = newColors.join("|");
elStyle.innerText = cssText;
theme_COLOR_config.colors = newColors;
}
// 直接全局替换样式方式进行切换
function replaceCssText(cssText, oldColors, newColors) {
oldColors.forEach(function (color, t) {
cssText = cssText.replace(
new RegExp(color.replace(/,/g, ",\\s*"), "ig"),
newColors[t]
); // 255, 255,3
});
return cssText;
}
alternate
<link href="reset.css" rel="stylesheet" type="text/css">
<link href="default.css" rel="stylesheet" type="text/css" title="默认">
<link href="red.css" rel="alternate stylesheet" type="text/css" title="红色">
<link href="green.css" rel="alternate stylesheet" type="text/css" title="绿色">
上面 4 个 <link>
元素,共出现了 3 中不同性质的 CSS 样式文件加载:
-
没有 title 属性,rel 属性值仅仅是 stylesheet 的
<link>
无论如何都会加载并渲染,如 reset.css; -
有 title 属性,rel 属性值仅仅是 stylesheet 的作为默认样式 CSS 文件加载并渲染,如 default.css;
-
有 title 属性,rel 属性值同时包含 alternate stylesheet 的作为备选样式 CSS 文件加载,默认不渲染,如 red.css 和 green.css;
这里有个非常有趣的特性,那就是 rel=“stylesheet"的如果有 title 属性并有值,性质上就变成了一个可以控制其渲染或者不渲染的特殊元素了。
使用 JavaScript 代码修改元素 DOM 对象的 disabled 值为 false,可以让默认不渲染的 CSS 开始渲染。注意,必须是 DOM 元素对象的 disabled 属性,而不是 HTML 元素的 disabled 属性,元素是没有 disabled 属性的。
// 渲染 red.css 这个皮肤
document.querySelector('link[href="red.css"]').disabled = false;
全局变量
Links
- https://alligator.io/css/theming-custom-properties/
- https://link.medium.com/QSCFGp1MzZ Theming in React with Styled Components