详解16个CSS新特性(2021最新版-上)

 2021 CSS 的新特性和以前两年的相比有类似也有不一样,本文就带你们看看今年的 CSS 到底说了什么。javascript



简介php


若是您有关注过这两年的 CSS 发展状态报告(2019年和2020年)的话,不难发现,在报告中有专门关于 CSS 新特性一项的介绍。
css



图片by 2020 年 CSS 发展状态报 告(https://2020.stateofcss.com/en-US/features/)


是否是有种似有相识的感受,或者有一些又是那么的陌生。

对于我而言,这些都不陌生。不过我想说的是上图(或者报告)中提到的只是冰山一角,有不少是没有出如今调查问卷和报告中。

好比接下来内容中提到的伪类选择器,内容可见性,容器查询等等。

嗯!先从 CSS 选择器开始!(^_^)


CSS伪类选择器html


CSS 选择发展到今日,能够说是一个庞大的体系了:前端


图片by  @linxz 的博客java


如今描述 CSS 选择器的规范主要有 CSS (指的是 CSS 2.1),后面分为选择器 Level 3 和 Level 4。伪类选择器在这三部分规范中都有出现过,他们是一个递进的过程,特别是在 Level 3 和 Level 4,在原有的基础上新增了很多优秀的选择器。今天要提到的几个现代伪类选择器就是出于 Levele 4:ios



  :is() 和 :where()


先来看 :is() 和 :where() 。Elad Shechter 曾在推特上发了一个关于 :is() 和 :where() 选择器的测试题:


若是你是第一次看到这样的测试题,请先自测一下,若是是你,你会选择哪一个答案(greenpurplered 仍是 blue)?

若是你选择的是 purple ,那么要恭喜你,你答对了。 

Demo 地址:https://codepen.io/airen/full/ExWxbKe

示例中出现了 :is() 和 :where() 两个可能你从未接触过的伪类选择器:

:is(.header, .main) .p { color: purple}:where(.header, .main) .p { color: red;}

其实这两个选择器等同于:

.header .p,.main .p { color: purple;}.header .p,.main .p { color: red;}

他们惟一不一样之处,就是选择器权重不一样。:where() 的优先级老是为 0 ,可是 :is() 的优先级是由它的选择器列表中优先级最高的选择器决定的。那么上面的示例中,咱们能够用下图来清晰的描述他们之间的关系:

图片by   twitter

即,下面三个选择器选中的相同的元素:

.header .p,.main .p { // ...}
:is(.header, .main) .p { // ...}
:where(.header, .main) .p { // ...}

不一样的是他们的权重不一样,其中:

.header .p,.main .p { // ....}
:is(.header, .main) .p { // ...}

示例中的两种选择器具备相同的权重(即020);其中:

:where(.header, .main) .p { // ...}
.p { // ...}

上面示例代码中的选择器具备相同权重(即 010):


:is() 和 :where() 伪类选择器的出现,将会让咱们的选择器变得更简洁,好比下成这个 :is() 的示例:

// Level 0h1 { font-size: 30px;}// Level 1 section h1, article h1, aside h1, nav h1 { font-size: 25px;}// Level 2 section section h1, section article h1, section aside h1, section nav h1,article section h1, article article h1, article aside h1, article nav h1,aside section h1, aside article h1, aside aside h1, aside nav h1,nav section h1, nav article h1, nav aside h1, nav nav h1, { font-size: 20px;}

使用 :is() 能够像下面这样来描述:

// Level 0h1 { font-size: 30px;}// Level 1:is(section, article, aside, nav) h1 { font-size: 25px;}// Level 2 :is(section, article, aside, nav):is(section, article, aside, nav) h1 { font-size: 20px;}

回过头来,请你们再看看下面这个测试题,请问 p 文本的颜色是什么?

<!-- HTML --><main class="main"> <p class="p">What is the text color?</p></main>// CSS:where(.header, .main) .p { color: red;}.p { color: blue;}:is(.header, .main) .p { color: purple;}.header .p,.main .p { color: green;}
查看答案:https://codepen.io/airen/full/dyvyJmW


 :not() 和 :has()


或许你们平时在开发前端页面的时候,碰到相似下图这样的需求:web



但愿最后一张卡片没有margin-bottom (或第一张卡片没有 margin-top)。

针对于这样的场景,:not() 选择器就很是有优点。为何这么说呢?

在没有 :not() 选择器的时候,你可能会想到下面这样的方式:

.card + .card { margin-top: 20px;} 
// 或
.card { margin-bottom: 20px;}
.card.card--last { // 也可能会使用 .card:last-child margin-bottom: 0;}

若是换成 :not() 选择器,能够这要来实现:

.card:not(:last-child) { margin-bottom: 20px}

效果以下:

Demo URL: https://codepen.io/airen/full/MPNLEo

虽然 CSS 选择器已经很是强大了,但一直以来,在 CSS 中没有从子元素选到父元素的样的选择器(父选择器):

有人提出但愿有一个 :parents() 这样的选择器!typescript


虽然直到目前为止尚未 :parents() 选择器,但庆幸的是,:has() 选择器即将到来,它能够用来选择父级元素。

目前 Igalia 公司正在为 Chrome 实现该选择器,其团队成员 Brian Kardell 新发表的《Can I :has()》文章中对 :has() 选择器进行了详细的阐述。


<section><!-- section 边框颜色是 blue,margin-bottom是 30px -->  <h1>H1 Level Title</h1></section> 
<section><!-- section 边框颜色是 #09f,margin-bottom是 30px --> <h1>H2 Level Title</h1></section>
<section><!-- section 边框颜色是 red --> <p>Text Paragraphs</p></section> /* CSS */
// 将匹配含有h1子元素的 section元素section:has(h1) { border-color: blue;}// 将匹配含有h2子元素的 section元素section:has(h2) { border-color: #09f;}// 将匹配含有p子元素的 section元素section:has(p) { border-color: red;}// 将匹配除了含有p子元素的 section元素section:not(:has(p)) { margin-bottom: 30px;}


在支持:has()的浏览器中,你将看到下图这样的效果:canvas



示例中还演示了 :not():has() 组合在一块儿使用的,但二者组合在一块儿所达到的意思却彻底不同。


其中 :not(:has(selector)) 匹配不含有 selector 元素的父元素,而 :has(:not(selector)) 匹配含有的不是 selector 子元素的元素。


二者主要区别在于,:has(:not(selector)) 写法必需要含有一个子元素,而 :not(:has()) 能够不含有元素也会被匹配。


有意思的是,Github 中有一个 Issue 在讨论** **:has-child()** 选择器**,或许哪一天,咱们在组件中就能够这样使用:



  :empty 和 :blank


在现代 Web 的开发的过程当中,老是没法避免数据吐出为空的状况,此时每每会给咱们的 UI 带来额外的麻烦。好比说,在同一类的 UI 元件中编写了必定的样式规则,但数据为空,此时在页面上可能会出现这样的场景:

CSS 的 :empty 和 :blank两个伪类选择器能够帮助咱们避免这种现象。这两个选择器都颇有用:


  • 给空元素添加样式

  • 建立空的状态


既然都是能够为空元素添加样式,那何为空元素,他们之间的差别又是什么?先来回答第一个问题,何为空元素?空元素是指元素没有任何子元素或子节点,好比:


<!-- 空元素 --><div class="error"></div><div class="error"><!-- 注释 --></div>
<!-- 非空元素 --><div class="error"> </div><!-- 中间有一个空格符 --><div class="error"></div><!-- 断行 --><div class="error"> <!-- 注释 --></div><!-- 注释断行排列 --><div class="error"><span></span></div>


:empty 和 :blank 相比,:empty 只能选中没有子元素的元素。子元素只能够是元素节点或文本(包括空格)。注释或处理指令都不会产生影响。


Demo: https://codepen.io/airen/full/yLMLKyo


注意,在空元素上即便使用伪元素 ::before ::after 建立内容,也能被:empty 识别。


:blank 要比 :empty 灵活地多,只要该元素中无任何子元素都能被识别。


不过 W3C 规范对该伪类选择器的定义更趋向于做用到表单控件中,好比用户没有在 inputtextarea 中输入内容时,提交表单能被识别到。有点相似于表单验证的功能。


早在 2018 年 Zell Liew 在推特上就针对 :empty:blank 作过相关的讨论,并发表一了篇有关于这方面的博文《:empty and :blank》:



到目前为止,:empty 已获得主流浏览器支持,能够用于实际生产中,但 :blank 伪类选择器仍是存在必定的争议的。


  :focus-visible 和 :focus-within


CSSer 对于 :focus 应该不会感到陌生,早期对于可聚焦元素可使用 :focus 来给元素设置焦点状态下的 UI 风格,即焦点环样式:


Chrome 86 开始,另外引入了 :focus-visible 和 :focus-within 两种伪类选择器来帮助咱们更好的控制 UI 焦点环的样式:



虽然 :focus :focus-within:focus-visible 均可以用来设置焦点环的样式,但他们之间仍是有必定的差别:

  • :focus :当用户使用鼠标点击焦点元素或使用键盘的 Tab 键(或快捷键)触发焦点元素焦点环的样式
  • :focus-visible :只有使用键盘的 Tab 键(或快捷键)触发焦点元素焦点环的样式。若是仅使用 :focus-visible 设置焦点环样式的话,那么用户使用鼠标点击焦点元素时不会触发焦点环样式
  • :focus-within:表示一个元素得到焦点,或该元素的后代元素得到焦点。这也意味着,它或它的后代得到焦点,均可以触发 :focus-within

来简单的看一个示例:


button:focus {  outline: 2px dotted #09f;  outline-offset: 2px; } 
button:focus-visible { outline: 2px solid #f36; outline-offset: 2px; }


你会发现,分别使用鼠标点击按钮和按Tab 让按钮得到焦点时焦点环样式效果不一样: 



不过须要注意的是,:focus 和 :focus-visible 也会涉及到选择器权重的问题,就上面的示例来讲,若是咱们把 :focus 选择器对应的样式放置到 :focus-visible 以后:


button:focus-visible {  outline: 2px solid #f36;  outline-offset: 2px; }
button:focus { outline: 2px dotted #09f; outline-offset: 2px; }


这个时候,你会发现无论用户使用键盘 Tab 键仍是鼠标让 <button> 得到焦点时,焦点样式都会采用 :focus 对应的样式:



若是咱们要让 :focus 和 :focus-visible 能够有独自的样式,能够借助CSS选择器中的 :not() 来处理:


button:focus:not(:focus-visible) { outline: 2px dotted #416dea; outline-offset: 2px; box-shadow: 0px 1px 1px #416dea;}
button:focus-visible { outline: 2px solid #416dea; outline-offset: 2px; box-shadow: 0px 1px 1px #416dea;}


这个时候按 Tab 键盘和鼠标点击时焦点环样式就有相应的差别:


Demo: https://codepen.io/airen/full/YzGdoLq


或许你想到这样的场景了,若是你在作 A11Y 方面的优化,但愿在移动端和PC端上对焦点元素设置不一样的焦点环样式,那用上面这种方案来讲就会很是的灵活。

对于 :focus-within 而言,它其实有点相似于 :has() 伪类选择器,能够在子元素获得焦点时,触发父元素:


上图描述了 :focus-within 和 :focus 之间的差别。更简单的说,它有点相似于 JavaScript 的事件冒泡,从可得到焦点元素开始一直冒泡到HTML的根元素 <html> ,均可以接收触发 :focus-within 事件。好比:


.box:focus-within,.container:focus-within { box-shadow: 0 0 5px 3px rgb(255 255 255 / 0.65);}
body:focus-within { background-color: #2a0f5bde;}
html:focus-within { border: 5px solid #09f;}复制代码


Demo: https://codepen.io/airen/full/JjRxoLE


借助于 :focus-within 伪类选择器的特性,咱们就可让一些交互效果变得更为简单,好比下面这个示例,使用 :focus-within 就能够实现,再也不须要任何 JavaScript 代码:

Demo: https://codepen.io/airen/full/KrPLmV


有了 :focus:focus-visible :focus-within 会让咱们更好的管理焦点元素在焦点状态下焦点环的 UI 效果。

在 CSS 的世界中,除了这里提到的伪类选择器以外,还有不少其余的伪类选择器(或伪元素),好比 ::marker:in-range:out-of-range



若是你感兴趣的话,能够尝试着使用 :in-range:out-of-range 给一些 input (好比 type numberrange input 元素,他们都有 min max 属性的设置) 在用户输入范围值和非范围值时,提供不一样的 UI 效果。


CSS 颜色


CSS 颜色模块自从 Level 4 开始,除了新增了一些新的函数值,好比 hwb()lch()lab()color-mix()color-contrast()color() 以外,对本来的 rgb()hsl() 以及 #rrggbb 等颜色值定义语法规则也作出调整。


好比,本来咱们熟悉的描述颜色值的方式:


#09f #90eafergb(123, 123, 123)rgba(123, 123, 123, .5)hsl(220, 50%, 50%)hsla(220, 50%, 50%, .5)


都有了新的语法规则,特别是对于 rgb()rgba()hsl() hsla() ,以往用逗号(,)做为分割符,如今能够直接使用空格作分割符,而且rgb()hsl() 函数在第三个值和第四个值之间加上 / 能够取替 rgba()hsla()



另外,十六进制描述颜色,也能够在本来的语法规则中最后两位添加新的位值来描述透度明。好比 #rrggbbaa 或 #rgba 。在Chrome 浏览器代码审查器中,已经能够看到这种语法规则的身影了: 



也就是说,咱们如今能够这样来描述颜色值:


#hex-with-alpha { color: #0f08; color: #00ff0088;}
#functional-notation { color: hsl(0deg 0% 0%); color: hsl(2rad 50% 50% / 80%); color: rgb(0 0 0); color: rgb(255 255 255 / .25);}
#lab-and-lch { --ux-gray: lch(50% 0 0); --rad-pink: lch(50% 200 230); --rad-pink: lab(150% 160 0); --pale-purple: lab(75% 50 -50);}


另外,咱们如今描述颜色都是在sRBG 色值空间,而颜色色值空间是一个复杂的体系,除了 sRGB 以外还有其余的色值空间,好比说 LCH



正如上图所示,LCH 颜色空间的颜色数量要比 sRGB 颜色空间的多,并且描述的颜色更为细腻。



如上图所示,能够在color() 函数中指定颜色空间:


#color-function { --rad-pink: color(display-p3 1 0 1); --rad-pink: color(lab 50% 150 -50); --rad-pink: color(srgb 100% 0% 50%);}


注意,使用了color() 函数指定颜色空间除了要考虑该函数支持度(浏览器的兼容性)还须要考虑硬件设备对颜色空间的支持度。

在 CSS 中能够借助媒体查询 @media 来作相应的判断,好比下面的示例,若是终端设备支持的话,就会采用指定颜色空间的色值:


@media (dynamic-range: high) { .neon-pink { --neon-glow: color(display-p3 1 0 1); }
.neon-green { --neon-grow: color(display-p3 0 1 0); }}


更为强大的是 CSS 颜色模块 Level 5 版本,对颜色函数能力作了进一步的扩展。好比,能够在 rgb()hsl() 、hwb() 、lab() 和 lch() 函数基础上添加 from 关键词,实现基于一个颜色的基础上,只对某个参数作调整:


rgb() = rgb([from <color>]? <percentage>{3} [ / <alpha-value> ]? ) | rgb([from <color>]? <number>{3} [ / <alpha-value> ]? ) hsl() = hsl([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) hwb() = hwb([from <color>]? <hue> <percentage> <percentage> [ / <alpha-value> ]? ) lab() = lab([from <color>]? <percentage> <number> <number> [ / <alpha-value> ]? ) lch() = lch([from <color>]? <percentage> <number> <hue> [ / <alpha-value> ]? )复制代码


来看一个 hsl() 的示例:



上图展现的是,基于 --theme-primary (原色,即 hsl(274, 61%, 50%))颜色只对l 参数作调整,从 50% 调整到 30% ,从而得到一个新的颜色,即 hsl(274, 61%, 30%) 。使用这样的方式,咱们就能够很轻易的获取基于某个颜色参数改变得来的颜色面板:



除此以外, 颜色模块 Level 5 版本还新增了一些新的函数用来描述颜色,好比 color-mix() 、color-contrast() 、color-adjust() 等:


.color-mix { --pink: color-mix(red, white);
--brand: #0af; --text1: color-mix(var(--brand) 25%, black); --text2: color-mix(var(--brand) 40%, black);}
.color-contrast { color: color-contrast( var(--bg) vs black, white );}
--text-on-bg: color-contrast( var(--bg-blue-1) vs var(--text-subdued), var(--text-light), var(--text-lightest));
.color-adjust { --brand: #0af; --darker: color-adjust(var(--brand) lightness -50%); --lighter: color-adjust(var(--brand) lightness +50%);}



简单介绍一下这几个颜色函数。其中 color-mix() 函数接受两个 <color> 值,并在给定的颜色空间中以指定的数量返回它们混合的结果。若是没有特殊说明,不然在 lch() 颜色空间中进行混合。好比下图所展现的就是 在lch() 颜色空间(默认)中将 redyellow 混合,每一个 lch 通道的红色值占65%,黄色值占 35%,即 color-mix(red yellow 65%)



color-contrast() 函数颇有意思,它能够帮助咱们提升Web可访问性方面的能力。其主要做用是获取一个颜色值,并将其与其余颜色值的列表进行比较,从列表中选择对比度最高的一个。



好比 color-contrast(white vs red, white, green) ,分别会拿 redwhitegreenwhite 对比,其中 greenwhite 对比度最高,最终会取 green 颜色:



color-adjust() 函数可用来在给定颜色空间中经过指定的变换函数对该颜色进行调整,除非另有规定,不然调整是在 lch() 色彩空间中进行的。好比 color-adjust(#0af lightness +50%) 是颜色 #0aflch 颜色空间中高度增长 50%


也就是说,在不久的将来,这些颜色函数能够很好的帮助咱们控制Web中的颜色值。由于这些颜色函数都已成为主流浏览器的实验性属性,特别是 Safari 浏览器,对这方面的支持度要高于 Chrome 和 Firefox 浏览器。


CSS 背景


CSS 的 background 属性是一个简写属性,这个对于你们来讲再熟悉不过了。不过在这里着重想把 background-positionbackground-repeat 单独拿出来和你们聊聊。你可能会问,这两个属性又不是什么新属性了,有什么值得聊的呢?其实未必,这两个属性虽然老,但有几个小细节对于不少开发者来讲仍是会感到陌生的。我们先从 backgroundd-position 开始。


  background-position


background-position 主要是用来指定背景图片在容器中的位置。你们较为熟悉的是,背景图片左上角(顶点)相对于容器左上角计算,以下图所示:



不过,有的时候,但愿背景图片能相对于容器右侧边缘或底部边缘计算,好比下图:



要让背景图片距离容器右侧边缘和底部边缘都是 50px 。针对这样的场景,你或许首先会想到使用容器大小和背景图片大小进行计算,得出距离顶部和左侧边缘的距离,而后将计算出来的值运用于 background-position 中。固然,熟悉 CSS 的同窗或许会想到使用 calc() 函数:


:root { --xPosition: 50px; --yPosition: 50px;}
.container { background-position: calc(100% - var(--xPosition) calc(100% - var(--yPosition)))}


使用 calc() 动态计算要比经过容器和背景图片尺寸大小计算方便得多。


事实上呢?background-position 提供了另外一种特性,咱们能够经过关键词 top 、right 、bottom 和 left 来指定方向。好比咱们熟悉的用法 :


background-position: 50px 50px;
// 至关于background-position: top 50px  left 50px;


那么,咱们要实现上图的效果,就可使用 right 和 bottom 关键词,让事情变得很是简单:


:root { --xPosition: 50px; --yPosition: 50px;}
.container { background-position: right var(--xPosition) bottom var(--yPosition);}


最终效果以下:


Demo: codepen.io/airen/full/…


注,示例中左侧是使用 calc() 计算的效果,右侧使用的是关键词实现的效果。


background-position 使用还有一个小细节须要注意,即 background-position 采用百分比值。由于使用百分比值的 background-position 计算会相对于其余单位值复杂:



正如上图所示,背景图片原始尺寸是 100px x 100px ,容器的尺寸(使用该背景图片的元素)是 410px x 210px ,若是使用 background-position: 75% 50% 时,它的计算以下:


// background-position的x轴坐标x = (容器宽度 - 背景图片宽度) x background-position的x坐标的百分比值 = (410 - 100) x 75% = 232.5px
// background-position的y轴坐标y = (容器高度 - 背景图片高度) x background-position的y坐标的百分比值 = (210 - 100) x 50% = 55px


就该示例而言,background-position: 75% 50% 就至关于 bacckground-position: 232.5px 55px


注意,若是咱们把 background-sizebackground-position 取百分比值场景结合起来使用的时候,会让事情变得更为复杂,特别是background-size取值为covercontain 更会让你感到蛋疼。为何会这样呢?这已经超出来这篇文章所要探讨的话题,若是你感兴趣的话,能够以此为课题,深刻探讨一下。


  background-repeat


在 background-repeat 属性上除了可使用咱们熟悉的 no-repeat 和 repeat (或者 repeat-x 和 repeat-y) 以外还可使用 round 和 space 

咱们都知道,使用 repeat 的时候,有可能会形成背景图片在平铺的时候被裁剪。若是但愿背景图片在平铺时不被剪切,那么可使用 round 来替代,它的最大特点就是背景图片在平铺的时候会根据容器的宽高对背景图片作相应的尺寸调整。而 space 会留出相应的空间,即在保证背景图片不被裁剪的状况之下,对多出来的空间以空白的方式在背景图片之间留出。



针对于这些值,我录制了一个简单的视频,从视频的展现效果中能够更好的看出它们之间的差别:


Demo: https://codepen.io/airen/full/mdWyOOj


CSS 蒙层和剪切


若是你对设计或设计软件较为熟悉的话,对于蒙层和剪切不会感到陌生。设计师在作一些设计稿的时候,时常也会用到蒙层和剪切的能力。随着 CSS 的发展,在 CSS 的世界中也有了这两个特性,它们在 W3C 的 《CSS Masking Module Level 1》规范中定义,主要的做用以下图所示:



能够灵活的控制内容的显示区域。

蒙层和剪切对应的 CSS 属性就是 maskclip-path ,其中 mask 是一个简写属性,它的使用规则和 background 很是的类似。我将经过简单的示例来向你们展现它们能帮咱们作些什么。

先看 mask

Demo: https://codepen.io/airen/full/MdQrvR


视频中的 emoji 和文本残缺不全(看上去被啃了同样)。在没有mask 的能力以前,若是咱们要实现这样的效果几乎是不太可能,如今有了以后,实现起来就很是的简单。咱们只须要像下面这样的一张图片(用于mask-image 上的图片),即蒙层图:



h1 { mask-image: url(mask.png);}复制代码

并且咱们还能够借助 
mask  的合成能力,让多个蒙层作合成运算:



运用蒙层相关的能力,能够快速帮助咱们实现一些业务场景所需的效果:



除此以外还能够实现换肤的效果:


Demo: https://codepen.io/airen/full/yWvzYy


再来看剪切。在 CSS 中使用 clip-path 的能力,除了能够帮助咱们控制好所要展现的区域以外,还能够结合不一样的路径和函数值实现不规则的图形展现。好比 Clippy 所展现的效果:


好比在实际需求中,须要实现一些不规则,又是多状态下的 UI 效果,那么 clip-path 就会很方便:


并且使用clip-path 结合 transition 或 animation 能够在交互上作一些更好的动效:

左侧是原状态下效果,右侧是鼠标悬浮状态下效果:

Demo: https://codepen.io/airen/full/zYqbgRK


特别是,当clip-path 支持 path() 函数时,能够作得事情更多了,好比咱们想要绘制一个心形的 UI 效果:



另外就是把 clip-path 、float 和 CSS 的 Shapes 结合能够实现一些不规则的图文混排的布局效果:



CSS 混合模式


CSS 混合模式是个颇有意思的特性,目前主要有 mix-blend-modebackground-blend-mode 两个属性,前者是用于多个元素的合成,后者是用于多个背景的合成。使用它们能够实现一些特殊的效果,好比相似 Photoshop 中的滤镜效果:




采用混合模式特性,咱们能够轻易的实现产品图换色的效果:


Demo: https://codepen.io/kylewetton/full/OJLmJoV


这里简单地介绍一下,这个效果是怎么实现的。


首先咱们有一张相似下图的产品图:



经过 SVG 的能力,咱们描绘图一个纯黑色的 SVG 形状,形状和上图的沙发是相吻合的:



描绘出来的 SVG 代码并不复杂:


<svg id="js-couch" class="couch__overlay" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="none" width="1000" height="394">  <defs>  <path d="M996.35 77.55q-1.85-1.95-8.65-3.75l-62.4-17.1q-9.3-2.75-12.15-2.5-1.8.15-2.85.45l-.75.3q2.25-16.3 3.75-22.05 1.15-4.4 1.4-10.8.2-6.6-.7-10.85-1.25-5.65-3.1-7.8-2.95-3.35-9.65-2.7-5.95.6-39.3 1.7-38.3 1.25-39.45 1.3-10.25.5-126.75.5-5.05 0-54.2 1.3-45.8 1.25-54.05.95-19.45-.45-30.4-.7-20.2-.55-23.1-1.3-22.3-5.85-26.5 1.25-2.65 4.55-3.85 7.9-.6 1.7-.7 2.5-.65-2.2-2.05-4.55-2.75-4.65-6.45-5.2-3.85-.55-13.65-.4-7.4.1-12 .4-.4.05-18.7.9-16.55.8-19.15 1.1-3.4.4-14.6 1.1-11.3.75-13.05.65h-9.8q-8.65-.05-11.45-.4-2.85-.35-9.25-.6-6.7-.15-8.5-.25-2.7-.1-27.75-.1-25.1 0-29.6.1-92.35 1.15-99 1.65-5.15.4-20 0-15.3-.4-24.4-1.25-6.75-.6-21-1.55-12.95-.9-14.85-1.1-6.45-1.05-11.05-1.5-8.7-.85-12.85.5-5.45 1.75-8.1 4.65-3.2 3.4-2.9 8.6.25 4.65 2.1 11.8 1 3.8 2.55 9.1 1 3.85 2.35 10.1-.1 1-1.5 1-1.75 0-7.7.85-7.1 1-9.8 2.05-2.4.9-23 4.75-21.2 3.9-22.05 4.15-8.2 1.85-15.05 3.35Q7.4 69.1 5.65 70.3 2.5 72.45 2 73.1.6 75 .75 79.2q.15 4.15 1.3 12.75.9 6.85 1.45 10 .5 2.75 8.55 54 6.65 42.15 7.35 46.85 1.15 7.65 4.9 28.55 4.55 25.2 6.35 31.2 2.45 8.15 3.8 11.75 1.85 4.9 3.2 5.75 1.25.8 6.85.65 2.75-.05 5.3-.25l23.85.35q.1 0 1 .95t2 .95q1.9 0 3.4-1.4l23.1-.25 43.65.4q135.05 2.15 137.9 1.9 1.25-.1 72.9.5 72.45.65 76.85.45 8.1-.35 64 .4 143.35.95 146 1.1.55.05 75.3.3 74.7.3 79.8.6 8.65.5 68.25-.35l51.75.5 1.6.4q1.95.35 3.8.05 1.45-.25 3.5-.2 1.9 0 3.35-.3 2.1-.45 8.25-.8 6.25-.3 8.75-.05 1.7.2 8 1 5.75.3 7.4-1.75 1.75-2.2 4.95-10.85 2.8-7.55 4.05-12.4.65-2.5 3.6-17.2 2.75-13.75 3.15-14.8.45-1.25 4.45-22.85 4.05-22.4 4.4-24.4.3-1.45 3.75-25.2 3.35-23.2 4-26.3 1.15-5.5 2.35-18.8 1.4-15.7.8-23.7-.6-8.35-3.35-11.15z" id="a" />  </defs>  <use xlink:href="#a"/> </svg>


使用 CSS 自定义属性,配合少量的 JavaScript代码便可完成产品图换色的效果: 


:root { --fill: #f36;}
svg { fill: var(--fill); mix-blend-mode: multiply;}


咱们使用 mix-blend-mode 的 multiply 效果。另外,JavaScript 动态改变 --fill 的值代码也简单:


const inputEle = document.querySelector('input') 
inputEle.addEventListener('change', (e)=>{ document.documentElement.style.setProperty('--fill', e.target.value); })复制代码


很简单吧,并且你能够发挥你的想象空间,能够实现不少具备创意的效果。


CSS 自定义属性


CSS 自定义属性能够说是 CSS 的标配了,正如 W3C 规范所描述的,CSS 自定义属性又被称为 CSS 变量:



不过在这里要说的不是 CSS 自定义如何使用?要和你们说的是 CSS 自定义属性中的无效变量。CSS 自定义属性中的无效变量是颇有用的一个特性,它能够实现 1 (真)和 0 (假)的开关切换效果。换句话说,能够很好的实现不一样状态的 UI 效果。


一样地,在 W3C 规范中对无效变量有详细描述,但不仔细的话,仍是会忽略该特性。先来看规范是怎么描述无效变量的:



它的意思是:

当一个自定义属性的值是 **initial**时,**var()**** 函数不能使用它进行替换。除非 指定了一个有效的回退值,不然会使声明在计算值时无效。**


在 CSS 中注册自定义属性时是使用 -- 来注册的,能够给已注册的自定义属性赋值,包括空字符串,但其中有一个细节很是有意思:


:root { --invalid:; // 注意冒号和分号之间无空格符,也无任何字符 --valid: ; // 注意冒号和分号之间只有一个空格符}


其中,--invalid 自定义属性被称为无效变量,而 --valid 自定义属性是一个有效变量。使用上面这种方式来区分有效和无效变量对于开发者而言,可读性极差,并且有些文本编辑器可能会对代码按本身配置的规格格式化,有可能会形成 --invalid:; 变成 --invalid: ; (有空格)。为此,通常使用关键词 initial来显式声明一个自定义属性为无效变量:


:root { --invalid: initial; // 无效变量 --valid: ; // 有效变量}


若是不使用 var() 函数调用已注册的自定义属性的话,那么对于已注册的自定义属性而言,不会起任何做用。而 var() 函数有两个参数,第一个参数就是自定义属性,第二个参数是备用值。当第一个参数是个无效值时,会采用第二个参数。正因如此,对于已注册的无效自定义属性(即无效变量),好比 上面代码中的 --invalid 。那么 var() 没有提供备用值(第二个参数),则会使 CSS 样式规则(声明)在计算值时无效:


:root { --invalid: initial; // 无效变量 --valid: ; // 有效变量}
foo { background-color: var(--invalid); // 未提供备用值,则background-color 计算值无效 color: var(--invalid, red) // 提供了备用值,--invalid是无效变量,则会采用备用值 red}


先来看一个简单的示例:


<div class="element"> <i>Element</i></div>
<i>Element</i>
<style> .element { --color: red; // 只做用于 .element 元素及其后代元素 }
i { --foo: initial; // 无效变量 --color: var(--foo); // 无效变量 background-color: var(--color, orange); // --color是无效变量,采用备用值 orange }</style>


运行上面的代码,你将看到的效果以下图所示: 


Demo: https://codepen.io/airen/full/GRWJGvx


再来看另外一个示例:


<section>Element</section>
<style> :root { --initial: initial; // 无效变量 --valid: ; // 有效变量 } section { background-color: var(--initial, red); // 无效变量,会采用备用值 red color: var(--valid, red); // 有效变量,备用值被忽略 }</style>


所以,你看到的效果以下:


Demo: https://codepen.io/airen/full/poeJKpM


这个示例中 color 属性至关于设置了 color: ; 值,客户端在计算该值时将被忽略,所以会继承其祖先元素的 color 值,该例继承了 body 元素的 color black
你可能对 CSS 自定义属性中的无效变量有了必定的认知。咱们继续往下。
为了在使用 CSS 自定义属性中的无效变量让开发者更易于理解,@Lea Verou在《The --var: ; hack to toggle multiple values with one custom property》引入了相似于Switch的概念,即:



:root { --ON: initial; // 无效变量,至关于开启 var()的备用值 --OFF:; // 有效变量,至关于关闭 var()的备用值}


这样一来,咱们在作 UI 不一样状态切换时,就只须要对 --ON 和 --OFF 的切换。好比某篇文章中提供的示例:


:root { --ON: initial; --OFF: ;}
button { --is-raised: var(--OFF); border: 1px solid var(--is-raised, rgb(0 0 0 / 0.1)); background: var( --is-raised, linear-gradient(hsl(0 0% 100% / 0.3), transparent) ) hsl(200 100% 50%); box-shadow: var( --is-raised, 0 1px hsl(0 0% 100% / 0.8) inset, 0 0.1em 0.1em -0.1em rgb(0 0 0 / 0.2) ); text-shadow: var(--is-raised, 0 -1px 1px rgb(0 0 0 / 0.3));}
button:hover { --is-raised: var(--ON);}
button:active { box-shadow: var(--is-raised, 0 1px 0.2em black inset);}


最终效果以下:


Demo: https://codepen.io/airen/full/XWNYRga


上面这个示例算是简单的了。不过咱们能够利用该特性实现更复杂的 UI 效果。好比下面这个多种换肤效果:


label { --box-shadow: var(--ON); --box-shadow-active: var(--OFF); box-shadow: 0 0 0 3px var(--box-shadow, rgba(0, 0, 0, 0.05)) va r(--box-shadow-active, #2196f3); cursor: pointer;}
label.dark { background-color: var(--dark-bgcolor);}
label.light { background-color: var(--light-bgcolor);}
label.blue { background-color: var(--blue-bgcolor);}
#dark:checked ~ div .dark,#light:checked ~ div .light,#blue:checked ~ div .blue { --box-shadow: var(--OFF); --box-shadow-active: var(--ON);}
.nav { color: var(--light, var(--light-color)) var(--dark, var(--dark-color)) var(--blue, var(--blue-color)); background-color: var(--light, var(--light-bgcolor)) var(--dark, var(--dark-bgcolor)) var(--blue, var(--blue-bgcolor));}
a.active,a:hover { background-color: var(--light, var(--light-active-bgcolor)) var(--dark, var(--dark-active-bgcolor)) var(--blue, var(--blue-active-bgcolor));}
/* 设置切换开关 */:root { --ON: initial; --OFF: ;
/* Dark */ --dark-color: rgba(156, 163, 175, 1); --dark-bgcolor: rgba(17, 24, 39, 1); --dark-active-bgcolor: rgba(55, 65, 81, 1);
/* Light */ --light-color: rgba(55, 65, 81, 1); --light-bgcolor: rgba(243, 244, 246, 1); --light-active-bgcolor: rgba(209, 213, 219, 1);
/* Blue */ --blue-color: rgba(165, 180, 252, 1); --blue-bgcolor: rgba(49, 46, 129, 1); --blue-active-bgcolor: rgba(67, 56, 202, 1);}
#dark:checked ~ .nav { --light: var(--OFF); --dark: var(--ON); --blue: var(--OFF);}
/* 默认为Light */#light:checked ~ .nav { --light: var(--ON); --dark: var(--OFF); --blue: var(--OFF);}
#blue:checked ~ .nav { --light: var(--OFF); --dark: var(--OFF); --blue: var(--ON);}


效果以下:


Demo: https://codepen.io/airen/full/OJbEzzw

示例中没有使用任何 JavaScript 代码。是否是颇有意思。要是感兴趣的话,你也能够尝试着撸撸这样的效果。


因为篇幅缘由,本文只介绍部分属性,剩余部分请期待下期的《详解16个 CSS 新特性(2021最新版-下)》。


✿  拓展阅读


做者| 大貘
编辑| 橙子君
出品| 阿里巴巴新零售淘系技术


本文分享自微信公众号 - 淘系技术(AlibabaMTT)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索