其实
CSS Variables
并不是最近才出现的新生事物,早在2012年
W3C
就已经公布了
CSS Variables
的首个公开草案;2017年3月微软
Edge
浏览器也宣布支持 CSS 变量,此时所有主要浏览器都已经支持这个 CSS 新功能。
CSS Variables
本质是定义一系列样式属性,本质上和
color
和
font-size
属性是一样的,只是没有默认含义,且可以被其他属性引用,并且提供了
JavaScript
基层API进行管理。因此
CSS Variables
天生就是为
动态主题
而生的,能够在运行时直接以简洁明了,灵活的方式调整页面样式。
Antd 在
antd@4.17.0-alpha.0
推出了实验性的动态主题方案,就是采用
CSS Variables
的方式来实现的,接下来本文将结合
Antd动态主题
来仔细探究
CSS Variables
和动态主题的内在联系。
本文分为
CSS Variables基础
和
Antd动态主题底层探究
两部分,依据循序渐进的方式,先介绍基础原理,再介绍动态主题的底层原理,已经熟悉基础原理的同学请跳过这一部分,直接进入干货部分。
CSS Variables
基础
1、变量声明
声明变量时,变量名之前必须加两个连词线(
--
),之所以使用
--
这个符号来表示,是因为
$
被 Sass 用掉了,
@
被 Less 用掉了。
html {
--ant-primary-color : #1890ff ;
--ant-primary-color-hover : #40a9ff ;
--ant-primary-color-active : #096dd9 ;
--ant-primary-color-outline : rgba (24 , 144 , 255 , 0.2 );
后续的单词一般采用HTML的属性的声明方式,使用 -
进行链接。
CSS Variables 又叫做CSS 自定义属性 ,它的值类型和 CSS 属性值的类型是一样的,可以放入字符串、色值、时间等。
变量名大小写敏感,--header-color
和--Header-Color
是两个不同变量。
@keyframes waveEffect {
100% {
box-shadow : 0 0 0 var (--cmsAnt-primary-color);
box-shadow : 0 0 0 6px var (--antd-wave-shadow-color);
@media screen and (min-width : 768px ) {
body {
--primary : #F7EFD2 ;
--secondary : #7F583F ;
并且支持在响应式布局@media
和动画@keyframes
中使用。
同样支持变量依赖声明。
html {
--antd-wave-shadow-color : var (--ant-primary-color);
--scroll-bar : 0 ;
2、var()
函数
var()
是用于读取变量,并且支持第二个参数作为默认值,这是个很棒的设计。
color : var (--foo, #7F583F );
以下是注意事项:
1、 读取变量值只能作用于属性值,而不能作用于属性名。
.foo {
--side : margin-top;
var (--side ): 20px ;
2、可以字符串拼接,但是带单位的变量值不可以拼接
body :after {
content : '--screen-category : ' var (--screen-category);
假如假如需要带单位,则需要使用calc()
函数。
.foo {
--gap : 20 ;
margin-top : var (--gap)px;
margin-top : calc (var (--gap) * 1px );
如果变量值带有单位,就不能写成字符串。
.foo {
--foo : '20px' ;
font-size : var (--foo);
.foo {
--foo : 20px ;
font-size : var (--foo);
3、作用域
同一个 CSS 变量,可以在多个选择器内声明。读取的时候,优先级最好的声明生效。
<style >
html {
--color : pink;
:root { --color : blue; }
div { --color : green; }
#alert { --color : red; }
* { color : var (--color); }
</style >
<p > 蓝色</p >
<div > 绿色</div >
<div id ="alert" > 红色</div >
:root
除了优先级更高之外,其他和html
保持一致,因此p
标签生效的是:root
的作用域的变量定义,如果想对动态主题进行强制覆盖,目前动态主题方案一般是使用html
作用域下,则可以使用:root
作用域声明进行强制覆盖。
选择器的优先级则是按照 html
< div
< ID 选择器
。
4、兼容性处理
CSS variables
在一些旧版本主流浏览器或者IE浏览器是无法使用的,可以采用以下写法进行规避。
color : #7F583F ;
color : var (--primary);
或者使用@supports
进行检测。
@supports ( (--a : 0 )) {
@supports ( not (--a : 0 )) {
5、JavaScript 操作
首先 JavaScript 可以检测浏览器是否支持 CSS 变量。
const isSupported =
window .CSS &&
window .CSS .supports &&
window .CSS .supports ('--a' , 0 );
if (isSupported) {
} else {
JavaScript API 写法。
document .body .style .setProperty ('--primary' , '#7F583F' );
document .body .style .getPropertyValue ('--primary' ).trim ();
document .body .style .removeProperty ('--primary' );
这里补充一些其他文章没有提到的信息。
首先<style>
内定义的样式变量,无法通过 JavaScript API 来获取。
document .querySelector (':root' ).style.getPropertyValue('--color' )
document .querySelector (':root' ).style.setProperty('--color' , 'gray' )
document .querySelector (':root' ).style.getPropertyValue('--color' )
到这里大家就明白了其内在限制了。
Antd 动态主题底层探究
首先看下 Antd 动态主题的文档 ,最主要的改动是修改引入的样式文件。
-- import 'antd/dist/antd.min.css' ;
++ import 'antd/dist/antd.variable.min.css' ;
本文抓取了最终引入的文件内容如下:
html {
--ant-primary-color : #1890ff ;
--ant-primary-color-hover : #40a9ff ;
--ant-primary-color-active : #096dd9 ;
color : var (--ant-primary-color);
text-decoration : none;
background-color : transparent;
outline : none;
cursor : pointer;
transition : color 0.3s ;
-webkit-text-decoration -skip: objects;
这里就显而易见了,底层就是CSS Variables
的设计。下面的语法实际也是对上面的 CSS Variables
的修改。
ConfigProvider.config ({ prefixCls : 'custom' , theme : { primaryColor : '#25b864' , }, });
到这里你是不是会疑惑?CSS Variables
是不是唯一的动态主题实现方案?实际上并不是的,antd-theme-generator
库就是就是基于 Less api
的动态主题方案,有兴趣的可以详细看下我的另外一篇文章《高度兼容低版本的 antd 的动态主题方案》 。这篇文章具体介绍antd-theme-generator
库的优缺点以及如何解决其中的问题落实到实际项目中去。
CSS Variables
是不是唯一的动态主题实现方案?
正如上面所说,Less 基础 API 也对样式变量进行了支持,而且相对于CSS Variables
的语法丰富度更高。以 Antd Button 的样式文件为例。
@import '../../style/themes/index' ;
@import '../../style/mixins/index' ;
@import './mixin' ;
@btn-prefix-cls: ~'@{ant-prefix}-btn' ;
@btn-ghost-color: @text-color ;
@btn-ghost-bg: transparent;
@btn-ghost-border: @border-color-base ;
.@{btn-prefix-cls} {
& -primary {
.btn-primary ();
.@{btn-prefix-cls} -group & :not (:first-child):not (:last-child) {
border-right-color : @btn-group-border ;
border-left-color : @btn-group-border ;
& :disabled {
border-color : @btn-default-border ;
如果你直接在HTML中引入类似上面 less 样式文件,
<script > less = { env : "development" }; </script >
<script src ="less.js" data-env ="development" > </script >
那么你就可以通过下面的语法进行样式动态调整。
less.modifyVars ({
'@buttonFace' : '#5B83AD' ,
'@buttonText' : '#D9EEF2' ,
只是大家日常为了做到更好的浏览器兼容,没有直接使用 less 样式文件,都是使用经过编译后的 css 文件。
这里还要提到一点就是 Less 命令行支持 Less variables
到 CSS Variables
的转换,这个就相当实用,例如在 Antd 动态主题中提到可以使用以下命令重新生成一份新前缀的 css 文件。
lessc --js --modify-var ="ant-prefix=custom" antd/dist/antd.variable.less modified.css
更多的命令可以参考 Less 官网命令行使用 。
到这里为止,本文分析了 CSS Variables
和动态主题互为表里的关系,有道是纸上得来终觉浅,绝知此事要躬行,大家可以在实际项目中可以多体验下动态主题的相关方案,毕竟动态主题的用户体验要远远高于传统的主题定制。
就我自己的使用体验而言,相对于传统静态主题定制的方式,动态主题在实际使用过程中也存在以下局限性的。
性能差,无法进行压缩、混淆,文件体积大
和 css in js
的思想有一定的冲突,无法使用 css module
,实际代码开发体验不好
不适用于微前端等应用场景,CSS 隔离会遇到很大的麻烦
因此在决策是否使用动态主题时,还是要从自身项目的实际需要进行综合考量的,按需选取。
引入文献:
阮一峰老师-CSS 变量教程
W3C CSS Variables 官方文档
掘金-# 高度兼容低版本的 antd 的动态主题方案
道长王jj
JavaScript
7915
萌萌哒草头将军
Vue.js
React.js
414
魔术师卡颂
JavaScript
React.js
7956
fly_dream
JavaScript