Skip to main content

CSS换肤方案

概述

页面效果跟随 CSS 样式表的不同定义,实现皮肤效果切换。

原理

CSS StyleSheet

在 HTML 结构中,实时插入<style> 或者 <link> 标签,引入 CSS 资源,均可以影响页面渲染。

皮肤样式的基础

实现皮肤的切换,最根本的一点,是保证 HTML 结构不变,CSS className 不变。
通过相同的 CSS Selector,定义同一块区域的样式,完成样式替换。

举个例子:

<article class="article">
<p class="title">标题</p>
<div class="content">
<p>内容</p>
</div>
</article>

<style title="normal">
.article{
padding: 4px;
}
.title{
font-size: 18px;
font-weight: bold;
color: blue;
}
.content{
font-size: 12px;
color: #fff;
}
</style>

替换 style[title=normal] 的样式,需要保证作用于相同HTML节点 CSS选择器 的优先级,要大于等于已生效的样式优先级。
所以,一般都会保持 CSS选择器 的一致性。

比如以下用于替换的 style[title=special] 样式

<style title="special">
.article{
padding: 8px;
}
.title{
font-size: 20px;
font-weight: bold;
color: green;
}
.content{
font-size: 14px;
color: #333;
}
</style>

样式如何生效与失效

生效样式

插入<style> 或者 <link> 标签到 HTML 中,标签携带的内容,浏览器会进行读取,并渲染到页面上。

失效样式

  • 使用 BOM(浏览器对象模型)API 读取到 StyleSheet,设置 disabled 属性为 true
  • 从 HTML 结构中,删除 <style> 或者 <link> 标签
  • <link>标签的 rel 属性,设置为 alternate,或者 media 属性设置为 print

如何生成样式资源

本质上,样式资源就是一份 CSS 静态资源文件

接下来,会介绍几种生成样式资源的方式。

手工生成

人工手工编写 CSS,然后分成几个文件,供页面引入。

theme-noraml.css
theme-yellow.css
theme-blue.css

配合前文的样式生效与失效章节提供的方式,即可完成皮肤切换。

CSS变量

将 CSS 样式中的属性值,抽离出来,变成 CSS 变量。
再通过设置 CSS 变量的取值,实时切换皮肤效果。

/* 根,用于设置CSS变量 */
:root{
--primary-color: #666;
}
div{
color: var(--primary-clor);
}

通过 document.body.style.setProperty ,可以实时修改 CSS变量 的取值。

document.body.style.setProperty('--primary-clor', 'blue');

通过Less、Sass等CSS扩展语言

Less 和 Sass 都支持在浏览器端实时编译样式,并输出 CSS。
下面,以 Less 为例,进行分解:

在浏览器环境中引入 Less 和 模板Less :

<link rel="stylesheet/less" type="text/css" href="styles.less" />
<script src="[https://cdn.jsdelivr.net/npm/less@4](https://cdn.jsdelivr.net/npm/less@4)" ></script>

然后设置 Less 变量:

less.modifyVars({ 
'@buttonFace': '#5B83AD',
'@buttonText': '#D9EEF2'
});

Less 在接收到变量后,会自动重新编译,达到实时换肤的效果。

使用模板引擎

利用模板引擎的插值语法,对 CSS 文件中的属性值,进行替换

下面,以模板引擎 ejs 为例进行讲解:

生成模板

// template.txt
.article{
padding: <%= articlePadding %>;
}
.title{
font-size: <%= titleFontSize %>;
font-weight: <%= titleFontWeight %>;
color: <%= titleColor %>;
}
.content{
font-size: <%= contentFontSize %>;
color: <%= contentColor %>;
}

皮肤配置

// normal.json
{
"articlePadding": "4px",
"titleFontSize": "18px",
"titleFontWeight": "bold",
"titleColor": "#666",
"contentFontSize": "12px",
"contentColor": "#fff",
}

根据皮肤配置,产出CSS资源

import template from 'template.txt';
import normal from 'normal.json';

// 产出CSS
const normalCss = ejs.render(template, normal);

// 插入HTML
const head = document.head || document.getElementsByTagName('head')[0];
const style = document.createElement('style');
head.appendChild(style);
style.type = 'text/css';

if (style.styleSheet){
style.styleSheet.cssText = normalCss;
} else {
style.appendChild(document.createTextNode(css));
}

参考