Chrome 137版本悄悄上线了一个叫if()的CSS函数,这玩意儿能根据CSS变量的值来条件式地给属性赋值。打个比方,以前做主题切换得写一大堆媒体查询或者类名切换,现在直接在一个background属性里就能搞定不同主题的颜色,就像给样式表装了个if判断逻辑。
啥是if()函数
if()函数本质上是个条件判断器,它盯着某个CSS自定义属性(也就是变量)的值,匹配上了就用冒号前面的值,匹配不上就走else分支。这玩意儿跟编程里的三元运算符有点像,但专门为CSS样式定制。比如想根据--theme变量是”白天”还是”黑夜”来改背景色,if()就能让一行代码干两行甚至多行的活。
基础玩法搞起来
先搭个最简单的场景:页面背景色跟着一个变量走。变量值是"Shamrock"时候背景变绿,其他情况变黄。
:root {
/* 改这个值试试效果 */
--theme: "Shamrock";
body {
background: if(
style(--theme: "Shamrock"): hsl(146 50% 40%);
else: hsl(43 74% 64%)
);
color: if(
style(--theme: "Shamrock"): hsl(146 50% 3%);
else: hsl(43 74% 3%)
);
}
}这里有个容易踩坑的点:style()里面必须用--theme: "Shamrock"这种写法,冒号后面跟字符串值。如果写成--theme=Shamrock或者不带引号,整个条件就废了,浏览器直接跳过。另外else分支不能省,除非想好了默认值交给浏览器自己搞。
多主题切换实战
三个主题来回蹦跶怎么办?if()支持串联多个条件,跟else if一个德行。
:root {
/* 可选值: "Shamrock" | "Saffron" | "Amethyst" */
--theme: "Saffron";
body {
color: if(
style(--theme: "Shamrock"): hsl(146 50% 3%);
style(--theme: "Saffron"): hsl(43 74% 3%);
style(--theme: "Amethyst"): hsl(282 47% 3%)
);
background: if(
style(--theme: "Shamrock"): hsl(146 50% 40%);
style(--theme: "Saffron"): hsl(43 74% 64%);
style(--theme: "Amethyst"): hsl(282 47% 56%)
);
transition: 300ms;
}
}这段代码里没写else,所以万一--theme被设成了”茄紫色”,浏览器就按默认样式渲染。另外transition加在这里完全没问题,主题切换时的颜色变化会丝滑过渡,不用额外写任何动画逻辑。手残党注意:每个条件分支末尾的分号不能丢,最后一个分支后面可以不写分号但写上也没毛病。
变量套变量玩出花
直接硬编码色值后期改起来想骂人?把颜色抽成独立变量,再用if()去调用这些变量。
@property --calc {
syntax: "<number>";
initial-value: 0;
inherits: false;
}
:root {
--theme: "Shamrock";
/* 基础色板 */
--shamrock: hsl(146 50% 40%);
--saffron: hsl(43 74% 64%);
--amethyst: hsl(282 47% 56%);
/* 互补色(调低亮度) */
--shamrock-comp: hsl(from var(--shamrock) h s 3%);
--saffron-comp: hsl(from var(--saffron) h s 3%);
--amethyst-comp: hsl(from var(--amethyst) h s 3%);
--bg: if(
style(--theme: "Shamrock"): var(--shamrock);
style(--theme: "Saffron"): var(--saffron);
style(--theme: "Amethyst"): var(--amethyst)
);
--text: if(
style(--theme: "Shamrock"): var(--shamrock-comp);
style(--theme: "Saffron"): var(--saffron-comp);
style(--theme: "Amethyst"): var(--amethyst-comp)
);
body {
background: var(--bg);
color: var(--text);
transition: 300ms;
}
}这样搞的妙处是,以后换主题色只要改--shamrock那几个变量的值,if()那块代码碰都不用碰。不过有个大坑:如果想在if()里用calc()计算结果做条件,比如判断--calc是不是等于1,直接写style(--calc: 1)是不认账的。必须先用@property注册变量并指定syntax: "<number>",否则浏览器把--calc当字符串处理,永远匹配不上。
另一条路:容器样式查询
除了if(),还有个叫“容器样式查询”的骚操作也能干同样的事。区别在于:if()负责给单个属性挑值,容器查询负责给一堆属性批量改样式。
:root {
--theme: "Shamrock";
--shamrock: hsl(146 50% 40%);
--saffron: hsl(43 74% 64%);
--amethyst: hsl(282 47% 56%);
--shamrock-comp: hsl(from var(--shamrock) h s 3%);
--saffron-comp: hsl(from var(--saffron) h s 3%);
--amethyst-comp: hsl(from var(--amethyst) h s 3%);
body {
@container style(--theme: "Shamrock") {
--bg: light-dark(var(--shamrock), var(--shamrock-comp));
--text: light-dark(var(--shamrock-comp), var(--shamrock));
}
@container style(--theme: "Saffron") {
--bg: light-dark(var(--saffron), var(--saffron-comp));
--text: light-dark(var(--saffron-comp), var(--saffron));
}
@container style(--theme: "Amethyst") {
--bg: light-dark(var(--amethyst), var(--amethyst-comp));
--text: light-dark(var(--amethyst-comp), var(--amethyst));
}
background: var(--bg);
color: var(--text);
transition: 300ms;
}
}两种方案选哪个?如果只改一两个属性,if()写起来更短;如果某个主题下要同时改十来个属性,容器查询能把代码块收拢在一起,看着更整洁。另外注意容器查询需要把样式写在@container块里面,外面还得确保元素是个容器(默认body就是),否则不生效。
一些脑洞大开的野路子
if()还能干点正经选择器干不了的活。比如想检测某个变量存不存在,不存在就隐藏图标:
.icon {
display: if(
style(--icon-family): inline-block;
else: none
);
}或者搞个“如果俩变量值一样就加边框”的骚逻辑:
#s2 {
border-top: if(
style(--s2-bg: var(--s1-bg)): thin solid red
);
}这里要注意var()在style()里的用法:style(--s2-bg: var(--s1-bg))意思是检查--s2-bg的值是否跟--s1-bg当前值完全相等(包括类型和字符串内容)。如果俩变量都是#fff或者都是hsl(0 0% 100%),就算书写格式不一样也会判定为不相等,因为字符串比对是严格匹配的。
