level 6
yanpp9161
楼主
CSS基本用法
在前端领域,标准的实现总是比社区协议慢很多,前端框架最喜欢的$被Sass变量用完
了。最常用的@也被Less用完了。为了在Sass和Less中使用CSS变量,一般只能采用折
中的方式使用。
<style>
/*在body选择器中声明了两个变量*/
body{
--primary-color:red;
/*变量名大小写敏感,--primary-color和--PRIMARY-COLOR是两个不同变
量*/
--PRIMARY-COLOR:initial;
}
/**同一个CSS变量,可以在多个选择器内声明。优先级高的会替换优先级低的*/
main{
--primary-color:blue;
}
/**使用CSS变量*/
.text-primary{
/*var()函数用于读取变量。*/
color:var(--primary-color)
}
<style>
<!--呈现红色字体,body选择器的颜色-->
<divclass="text-primary">red</div>
<!--呈现蓝色字体,main选择器定义的颜色-->
<mainclass="text-primary">blue</main>
<!--呈现紫色字体,当前内联样式表的定义-->
<divstyle='--primary-color:purple"class="text-primary">purple</main>
这里我们可以看到针对同一个CSS变量,可以在多个选择器内声明。读取的时候,优
先级最高的声明生效。这与CSS的"层叠"(cascade)规则是一致的。
由于这个原因,全局的变量通常放在根元素:root里面,确保任何选择器都可以读取它
们。
:root{
--primary-color:#06c;
}
同时,CSS变量提供了JavaScript与CSS通信的方法。就是利用js操作css变
量。我们可以使用:
<style>
/*...和上面CSS一致*/
</style>
<!--呈现黄色字体-->
<divclass="text-primary">red</div>
<!--呈现蓝色字体,main选择器定义的颜色-->
<mainid='primary'class="text-primary">blue</main>
<!--呈现紫色字体,当前内联样式表的定义-->
<divid="secondary"style='--primary-color:purple"
class="text-primary">purple</main>
<script>
//设置变量
document.body.style.setProperty('--primary-color','yellow');
//设置变量,jsDOM元素ID就是全局变量,所以直接设置main即可
//变为红色
primary.style.setProperty('--primary-color','red');
//变为黄色,因为当前样式被移除了,使用body上面样式
secondary.style.removeProperty('--primary-color');
//通过动态计算获取变量值
getComputedStyle(document.body).getPropertyValue('--primary-color')
</script>
实现默认配置
如果让我来思考,我肯定无法想象出结合CSS预处理器+CSS变量便可以实现组件
样式的默认配置。这里我先介绍两个关于该功能的前置知识点:
事实上,CSS变量的var()函数还可以使用第二个参数,表示变量的默认值。如果该
变量此前没有定义或者是无效值,就会使用这个默认值。
/*没有设置过--primary-color,颜色默认使用#7F583F*/
color:var(--primary-color,#7F583F);
虽然目前CSS变量不是新的属性,但终究不是所有的浏览器都支持CSS变量的,这
里我们还是要考虑一下优雅降级。
/*对于不支持CSS变量的浏览器,可以采用下面的写法。*/
a{
/*颜色默认值*/
color:#7F583F;
/*不支持则不识别该条规则*/
color:var(--primary);
}
结合CSS处理器+CSS变量便可以实现组件样式的默认配置。这里参考了有赞的
VantWeapp[2]的做法。有赞代码**theme.less**[3]如下所示:
//先导入所有Less变量
@import(reference)'./var.less';
//利用正则去替换Less变量为CSS变量
.theme(@property,@imp){
@{property}:e(replace(@imp,'@([^()]+)','@{$1}','ig'));
@{property}:e(replace(@imp,'@([^()]+)','var(--$1,@{$1})','ig'));
}
函数效果如下所示:
@import'../common/style/theme.less';
.van-button{
//...其他省略
.theme(height,'@button-default-height');
.theme(line-height,'@button-line-height');
.theme(font-size,'@button-default-font-size');
}
//=>less编译之后生成
.van-button{
//...其他省略
height:44px;
height:var(--button-default-height,44px);
line-height:20px;
line-height:var(--button-line-height,20px);
font-size:16px;
font-size:var(--button-default-font-size,16px);
}
我们可以看到每调用一次Less函数将会被编译成两个属性。第一个属性的设定对于不
支持CSS变量的设备可以直接使用,如果当前设备支持CSS变量,则会使用CSS变
量,但是由于当前CSS变量未定义,就会使用变量的默认值。虽然
'@button-default-height虽然也是一个变量,但是该变量仅仅只是less变量,最终
生成的代码中并没有--button-default-height这样的变量。此时我们就可以在使用
样式的位置或者:root中添加变量--button-default-height。
这种方式更适合组件开发,因为该方案不声明任何css变量,只是预留的css变量名
称和默认属性。这样的话,无论开发者的选择器优先度有多低,代码都可以很容易的覆
盖默认属性。因为我们仅仅使用css的默认值。
大家可能有时候会想,这样的话,我们不是有更多的代码了吗?其实未必,事实上我们
可以直接在页面内部定义变量样式。其他组件直接通过style去使用页面内的变量。当
然了,事实上书写的代码多少,重点在于想要控制默认样式的粒度大小。粒度越小,则
需要在各个组件内部书写的变量越多,粒度大,我们也就不必考虑太多。
SpaceToggle逻辑切换
CSS没有逻辑切换似乎是一种共识,但是我们可以利用选框(单选与多选)和CSS变量
来实现判断逻辑。我们先来看看如何使用CSS变量。
<style>
.red-box{
--toggler:;
--red-if-toggler:var(--toggler)red;
background:var(--red-if-toggler,green);/*willbered!*/
}
.green-box{
--toggler:initial;
--red-if-toggler:var(--toggler)red;
background:var(--red-if-toggler,green);/*willbegreen!*/
}
</style>
<!--宽度高度为100px的红色盒子-->
<div
style="height:100px;width:100px"
class="red-box"
></div>
<!--宽度高度为100px的绿色盒子-->
<div
style="height:100px;width:100px"
class="green-box"
></div>
这里因为一个变量--toggler使用空格或者initial而产生了不同的结果,基于这样
的结果我们不难想象我们可以触发变量的修改而产生不同的结果。
他不是一个bug,也不是一个hack。他的原理完完全全的在CSSCustomProperties
规范[4]中。
Thisvalueserializesastheemptystring,butactuallywritinganemptyvalue
intoacustomproperty,like--foo:;,isavalid(empty)value,notthe
guaranteed-invalidvalue.If,forwhateverreason,onewantstomanuallyreseta
variabletotheguaranteed-invalidvalue,usingthekeywordinitialwilldothis.
标梵互动程序工程师解释如下,事实上-foo:;这个变量并不是一个无效值,它是一个
空值。initial才是CSS变量的无效值。其实这也可以理解,css没有所谓的空字符串,
空白也不代表着无效,只能使用特定值来表示该变量无效。这个时候,我们再回头来看
原来的CSS代码。
.red-box{
/*当前为空值*/
--toggler:;
/*因为var(--toggler)得到了空,所以得到结果为--red-if-toggler:red*/
--red-if-toggler:var(--toggler)red;
/**变量是red,不会使用green*/
background:var(--red-if-toggler,green);/*willbered!*/
}
.green-box{
/**当前为无效值*/
--toggler:initial;
/**仍旧无效数据,因为var只会在参数不是initial时候进行替换*/
--red-if-toggler:var(--toggler)red;
/**最终无效值没用,得到绿色*/
background:var(--red-if-toggler,green);/*willbegreen!*/
/*根据当前的功能,我们甚至可以做到and和or的逻辑
*--tog1--tog2--tog3同时为空值时是红色
*/
--red-if-togglersalltrue:var(--tog1)var(--tog2)var(--tog3)red;
/*
*--tog1--tog2--tog3任意为空值时是红色
*/
--red-if-anytogglertrue:var(--tog1,var(--tog2,var(--tog3)))red;
}
新式媒体查询
当我们需要开发响应式网站的时候,我们必须要使用媒体查询@media。先看一下用
传统的方式编写这个基本的响应式CSS:
.breakpoints-demo>*{
width:100%;
background:red;
}
@media(min-width:37.5em)and(max-width:56.249em){
.breakpoints-demo>*{
width:49%;
}
}
@media(min-width:56.25em)and(max-width:74.99em){
.breakpoints-demo>*{
width:32%;
}
}
@media(min-width:56.25em){
.breakpoints-demo>*{
background:green;
}
}
@media(min-width:75em){
.breakpoints-demo>*{
width:24%;
}
}
同样,我们可以利用css变量来优化代码结构,我们可以写出这样的代码:
/**移动优先的样式规则*/
.breakpoints-demo>*{
/**小于37.5em,宽度100%*/
--xs-width:var(--media-xs)100%;
/**小于56.249em,宽度49%*/
--sm-width:var(--media-sm)49%;
--md-width:var(--media-md)32%;
--lg-width:var(--media-gte-lg)24%;
width:var(--xs-width,var(--sm-width,var(--md-width,var(--lg-width))));
--sm-and-down-bg:var(--media-lte-sm)red;
--md-and-up-bg:var(--media-gte-md)green;
background:var(--sm-and-down-bg,var(--md-and-up-bg));
}
可以看出,第二种CSS代码非常清晰,数据和逻辑保持在一个CSS规则中,而不是
被@media切割到多个区块中。这样,不但更容易编写,也更加容易开发者读。详情
可以参考css-media-vars[5]。该代码库仅仅只有3kb大小,但是却是把整个编写代
码的风格修改的完全不同。原理如下所示:
/**
*css-media-vars
*BSD2-ClauseLicense
*Copyright(c)James0x57,PropJockey,2020
*/
html{
--media-print:initial;
--media-screen:initial;
--media-speech:initial;
--media-xs:initial;
--media-sm:initial;
--media-md:initial;
--media-lg:initial;
--media-xl:initial;
/*...*/
--media-pointer-fine:initial;
--media-pointer-none:initial;
}
/*把当前变量变为空值*/
@mediaprint{
html{--media-print:;}
}
@mediascreen{
html{--media-screen:;}
}
@mediaspeech{
html{--media-speech:;}
}
/*把当前变量变为空值*/
@media(max-width:37.499em){
html{
--media-xs:;
--media-lte-sm:;
--media-lte-md:;
--media-lte-lg:;
}
}
其他继CSS键盘记录器[6]暴露了CSS安全性问题之后,CSS变量又一次让我看到
了玩技术是怎么样的。CSSSpaceToggle技术不但可以应用于上面的功能,甚至还可
以编写UI库augmented-ui[7]以及扫雷[8]游戏。这简直让我眼界大开。在我有
限的开发生涯中,很难找到类似于css这种设计意图和使用方式差异如此之大的技术。
CSS是很有趣的,而CSS的有趣之处就在于最终呈现出来的技能强弱与你自身的思维
方式,创造力是密切相关的。上文只是介绍了CSS变量的一些玩法,也许有更多有意
思的玩法,不过这就需要大家的创造力了。
2020年12月29日 06点12分
1
在前端领域,标准的实现总是比社区协议慢很多,前端框架最喜欢的$被Sass变量用完
了。最常用的@也被Less用完了。为了在Sass和Less中使用CSS变量,一般只能采用折
中的方式使用。
<style>
/*在body选择器中声明了两个变量*/
body{
--primary-color:red;
/*变量名大小写敏感,--primary-color和--PRIMARY-COLOR是两个不同变
量*/
--PRIMARY-COLOR:initial;
}
/**同一个CSS变量,可以在多个选择器内声明。优先级高的会替换优先级低的*/
main{
--primary-color:blue;
}
/**使用CSS变量*/
.text-primary{
/*var()函数用于读取变量。*/
color:var(--primary-color)
}
<style>
<!--呈现红色字体,body选择器的颜色-->
<divclass="text-primary">red</div>
<!--呈现蓝色字体,main选择器定义的颜色-->
<mainclass="text-primary">blue</main>
<!--呈现紫色字体,当前内联样式表的定义-->
<divstyle='--primary-color:purple"class="text-primary">purple</main>
这里我们可以看到针对同一个CSS变量,可以在多个选择器内声明。读取的时候,优
先级最高的声明生效。这与CSS的"层叠"(cascade)规则是一致的。
由于这个原因,全局的变量通常放在根元素:root里面,确保任何选择器都可以读取它
们。
:root{
--primary-color:#06c;
}
同时,CSS变量提供了JavaScript与CSS通信的方法。就是利用js操作css变
量。我们可以使用:
<style>
/*...和上面CSS一致*/
</style>
<!--呈现黄色字体-->
<divclass="text-primary">red</div>
<!--呈现蓝色字体,main选择器定义的颜色-->
<mainid='primary'class="text-primary">blue</main>
<!--呈现紫色字体,当前内联样式表的定义-->
<divid="secondary"style='--primary-color:purple"
class="text-primary">purple</main>
<script>
//设置变量
document.body.style.setProperty('--primary-color','yellow');
//设置变量,jsDOM元素ID就是全局变量,所以直接设置main即可
//变为红色
primary.style.setProperty('--primary-color','red');
//变为黄色,因为当前样式被移除了,使用body上面样式
secondary.style.removeProperty('--primary-color');
//通过动态计算获取变量值
getComputedStyle(document.body).getPropertyValue('--primary-color')
</script>
实现默认配置
如果让我来思考,我肯定无法想象出结合CSS预处理器+CSS变量便可以实现组件
样式的默认配置。这里我先介绍两个关于该功能的前置知识点:
事实上,CSS变量的var()函数还可以使用第二个参数,表示变量的默认值。如果该
变量此前没有定义或者是无效值,就会使用这个默认值。
/*没有设置过--primary-color,颜色默认使用#7F583F*/
color:var(--primary-color,#7F583F);
虽然目前CSS变量不是新的属性,但终究不是所有的浏览器都支持CSS变量的,这
里我们还是要考虑一下优雅降级。
/*对于不支持CSS变量的浏览器,可以采用下面的写法。*/
a{
/*颜色默认值*/
color:#7F583F;
/*不支持则不识别该条规则*/
color:var(--primary);
}
结合CSS处理器+CSS变量便可以实现组件样式的默认配置。这里参考了有赞的
VantWeapp[2]的做法。有赞代码**theme.less**[3]如下所示:
//先导入所有Less变量
@import(reference)'./var.less';
//利用正则去替换Less变量为CSS变量
.theme(@property,@imp){
@{property}:e(replace(@imp,'@([^()]+)','@{$1}','ig'));
@{property}:e(replace(@imp,'@([^()]+)','var(--$1,@{$1})','ig'));
}
函数效果如下所示:
@import'../common/style/theme.less';
.van-button{
//...其他省略
.theme(height,'@button-default-height');
.theme(line-height,'@button-line-height');
.theme(font-size,'@button-default-font-size');
}
//=>less编译之后生成
.van-button{
//...其他省略
height:44px;
height:var(--button-default-height,44px);
line-height:20px;
line-height:var(--button-line-height,20px);
font-size:16px;
font-size:var(--button-default-font-size,16px);
}
我们可以看到每调用一次Less函数将会被编译成两个属性。第一个属性的设定对于不
支持CSS变量的设备可以直接使用,如果当前设备支持CSS变量,则会使用CSS变
量,但是由于当前CSS变量未定义,就会使用变量的默认值。虽然
'@button-default-height虽然也是一个变量,但是该变量仅仅只是less变量,最终
生成的代码中并没有--button-default-height这样的变量。此时我们就可以在使用
样式的位置或者:root中添加变量--button-default-height。
这种方式更适合组件开发,因为该方案不声明任何css变量,只是预留的css变量名
称和默认属性。这样的话,无论开发者的选择器优先度有多低,代码都可以很容易的覆
盖默认属性。因为我们仅仅使用css的默认值。
大家可能有时候会想,这样的话,我们不是有更多的代码了吗?其实未必,事实上我们
可以直接在页面内部定义变量样式。其他组件直接通过style去使用页面内的变量。当
然了,事实上书写的代码多少,重点在于想要控制默认样式的粒度大小。粒度越小,则
需要在各个组件内部书写的变量越多,粒度大,我们也就不必考虑太多。
SpaceToggle逻辑切换
CSS没有逻辑切换似乎是一种共识,但是我们可以利用选框(单选与多选)和CSS变量
来实现判断逻辑。我们先来看看如何使用CSS变量。
<style>
.red-box{
--toggler:;
--red-if-toggler:var(--toggler)red;
background:var(--red-if-toggler,green);/*willbered!*/
}
.green-box{
--toggler:initial;
--red-if-toggler:var(--toggler)red;
background:var(--red-if-toggler,green);/*willbegreen!*/
}
</style>
<!--宽度高度为100px的红色盒子-->
<div
style="height:100px;width:100px"
class="red-box"
></div>
<!--宽度高度为100px的绿色盒子-->
<div
style="height:100px;width:100px"
class="green-box"
></div>
这里因为一个变量--toggler使用空格或者initial而产生了不同的结果,基于这样
的结果我们不难想象我们可以触发变量的修改而产生不同的结果。
他不是一个bug,也不是一个hack。他的原理完完全全的在CSSCustomProperties
规范[4]中。
Thisvalueserializesastheemptystring,butactuallywritinganemptyvalue
intoacustomproperty,like--foo:;,isavalid(empty)value,notthe
guaranteed-invalidvalue.If,forwhateverreason,onewantstomanuallyreseta
variabletotheguaranteed-invalidvalue,usingthekeywordinitialwilldothis.
标梵互动程序工程师解释如下,事实上-foo:;这个变量并不是一个无效值,它是一个
空值。initial才是CSS变量的无效值。其实这也可以理解,css没有所谓的空字符串,
空白也不代表着无效,只能使用特定值来表示该变量无效。这个时候,我们再回头来看
原来的CSS代码。
.red-box{
/*当前为空值*/
--toggler:;
/*因为var(--toggler)得到了空,所以得到结果为--red-if-toggler:red*/
--red-if-toggler:var(--toggler)red;
/**变量是red,不会使用green*/
background:var(--red-if-toggler,green);/*willbered!*/
}
.green-box{
/**当前为无效值*/
--toggler:initial;
/**仍旧无效数据,因为var只会在参数不是initial时候进行替换*/
--red-if-toggler:var(--toggler)red;
/**最终无效值没用,得到绿色*/
background:var(--red-if-toggler,green);/*willbegreen!*/
/*根据当前的功能,我们甚至可以做到and和or的逻辑
*--tog1--tog2--tog3同时为空值时是红色
*/
--red-if-togglersalltrue:var(--tog1)var(--tog2)var(--tog3)red;
/*
*--tog1--tog2--tog3任意为空值时是红色
*/
--red-if-anytogglertrue:var(--tog1,var(--tog2,var(--tog3)))red;
}
新式媒体查询
当我们需要开发响应式网站的时候,我们必须要使用媒体查询@media。先看一下用
传统的方式编写这个基本的响应式CSS:
.breakpoints-demo>*{
width:100%;
background:red;
}
@media(min-width:37.5em)and(max-width:56.249em){
.breakpoints-demo>*{
width:49%;
}
}
@media(min-width:56.25em)and(max-width:74.99em){
.breakpoints-demo>*{
width:32%;
}
}
@media(min-width:56.25em){
.breakpoints-demo>*{
background:green;
}
}
@media(min-width:75em){
.breakpoints-demo>*{
width:24%;
}
}
同样,我们可以利用css变量来优化代码结构,我们可以写出这样的代码:
/**移动优先的样式规则*/
.breakpoints-demo>*{
/**小于37.5em,宽度100%*/
--xs-width:var(--media-xs)100%;
/**小于56.249em,宽度49%*/
--sm-width:var(--media-sm)49%;
--md-width:var(--media-md)32%;
--lg-width:var(--media-gte-lg)24%;
width:var(--xs-width,var(--sm-width,var(--md-width,var(--lg-width))));
--sm-and-down-bg:var(--media-lte-sm)red;
--md-and-up-bg:var(--media-gte-md)green;
background:var(--sm-and-down-bg,var(--md-and-up-bg));
}
可以看出,第二种CSS代码非常清晰,数据和逻辑保持在一个CSS规则中,而不是
被@media切割到多个区块中。这样,不但更容易编写,也更加容易开发者读。详情
可以参考css-media-vars[5]。该代码库仅仅只有3kb大小,但是却是把整个编写代
码的风格修改的完全不同。原理如下所示:
/**
*css-media-vars
*BSD2-ClauseLicense
*Copyright(c)James0x57,PropJockey,2020
*/
html{
--media-print:initial;
--media-screen:initial;
--media-speech:initial;
--media-xs:initial;
--media-sm:initial;
--media-md:initial;
--media-lg:initial;
--media-xl:initial;
/*...*/
--media-pointer-fine:initial;
--media-pointer-none:initial;
}
/*把当前变量变为空值*/
@mediaprint{
html{--media-print:;}
}
@mediascreen{
html{--media-screen:;}
}
@mediaspeech{
html{--media-speech:;}
}
/*把当前变量变为空值*/
@media(max-width:37.499em){
html{
--media-xs:;
--media-lte-sm:;
--media-lte-md:;
--media-lte-lg:;
}
}
其他继CSS键盘记录器[6]暴露了CSS安全性问题之后,CSS变量又一次让我看到
了玩技术是怎么样的。CSSSpaceToggle技术不但可以应用于上面的功能,甚至还可
以编写UI库augmented-ui[7]以及扫雷[8]游戏。这简直让我眼界大开。在我有
限的开发生涯中,很难找到类似于css这种设计意图和使用方式差异如此之大的技术。
CSS是很有趣的,而CSS的有趣之处就在于最终呈现出来的技能强弱与你自身的思维
方式,创造力是密切相关的。上文只是介绍了CSS变量的一些玩法,也许有更多有意
思的玩法,不过这就需要大家的创造力了。