我们来聊一聊,在今年CSS都会新增哪些新特性。其中有些特性已经在主流浏览器中得到了支持,有些还在实验性阶段,但也会随着时间的推移慢慢而来。
容器查询(Container Queries)
介绍
容器查询@container类似于媒体查询@media,区别在于查询所依据的对象不同。媒体查询依据的是浏览器的视窗大小,容器查询依据的是元素的父元素或者祖先元素的大小。
有关容器查询的属性一共有三个,分别是container-type、container-name、container。
container-type:标识一个作为被查询的容器,取值范围为size、inline-size、block-size(size表示水平和垂直方向都建立,inline-size是只在水平方向建立,block-size是只在垂直方向建立)。
container-name:被查询的容器的名字
container:container-type和container-name的简写
main {
container: inline-size / name;
/*
* 等价于
* container-type: inline-size;
* container-name: name;
*/
}
如果有多个容器的写法:
.container-a {
container: inline-size aside;
}
.container-b {
container: inline-size banner;
}
@container banner (max-width: 480px) {
p {
font-weight: bold;
}
}
例子
或者随着容器大小改变字体大小:
<div className="wrap">
<div className="g-container">
<div className="child">Title</div>
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Necessitatibus vel eligendi, esse illum similique sint!!
</p>
</div>
</div>
.wrap {
width: 500px;
resize: horizontal;
overflow: auto;
}
.g-container {
display: flex;
flex-wrap: nowrap;
.child {
background-color: #f90;
min-width: 200px;
display: flex;
}
}
.wrap {
container-name: wrap;
container-type: inline-size;
}
@container wrap (max-width: 400px) {
.g-container {
flex-wrap: wrap;
flex-direction: column;
}
}
限制:目前,您不能使用基于高度的容器查询。
浏览器支持情况
级联层(Cascade Layers)
介绍
有时候当我们想要覆盖组件原来的样式来应用我们自定义的样式时,一般情况下我们会采用优先级更高的样式名来进行覆盖(或ID选择器或嵌套很多层),有时候又不得不应用!important ,这样很容易造成样式的混乱,不好管理。
级联层的诞生就是为了解决上述问题,它可以让CSS样式按照我们定义好的级联顺序展示,起到控制不同样式间的优先级的作用。
使用方法
通过@layer可以定义一个级联层。如下我们就定义了一个名字为A的级联层。
<template>
<div id="app">hello world</div>
</template>
<style>
#app {
width:100px;
height: 100px;
}
// 创建一个名为 A 的级联层
@layer A {
div {
background-color: red;
}
}
</style>
如果页面内的 @layer 太多,可能不太好记住所有 @layer 的顺序,因此,还有这样一种写法。我们可以同时命名多个 @layer 层,其后再补充其中的样式规则。
@layer A, B, C;
@layer A {
div {
background-color: green;
}
}
@layer B {
#app {
background-color: blue;
}
}
@layer C {
div {
background-color: red;
}
}
1, 上述代码,我们首先定义了 @layer A,B, C, 三个 @layer 级联层。而后再后面的 CSS 代码中补充了每个级联层的 CSS 代码,但是样式的优先级为:C > B > A 因此,最终的 div 的颜色值为 @layer C 中定义的颜色,为 red
2, 即使我们在级联层B中使用了ID选择器。所以最后div将展示红色的背景色(无视选择器)。
一个级联层可以通过 @import 来创建,规则存在于被引入的样式表内:
@import(utilities.css) layer(utilities);
非@layer层与@layer层的优先级
当然,我们还得考虑到,没有被 @layer 包裹的样式,它的优先级和被 @layer 包裹的样式,哪个优先级更高?
试验一下:
HTML
<body>
I'm using @layers.
</body>
CSS
body {
background-color: bisque;
}
@layer A {
body {
background-color: red;
color: black;
}
}
@layer B {
body {
color: blue;
background-color: green;
}
}
网页效果为:
可以看到:非 @layer 包裹的样式,就算写在其他layer之前,也拥有比 @layer 包裹样式更高的优先级。
嵌套层
CSS原生的嵌套虽然还没实装,但是草案已经出来蛮长的时间了,@layer 也是支持嵌套的:
@layer A {
@layer B{
...
}
}
等价于:
@layer A.B {
...
}
这里要注意一下, A 的优先级是高于 A.B 的:
@layer A {
div {
background: blue;
}
@layer B {
div {
background: red;
}
}
}
body 最终背景色为 blue ,这个也好理解,假设没有 A 这层 layer 包裹,那么实际上就是 layer 和非 layer 的优先级较量,非layer的更高,对吧。
而如果存在 layer 多级嵌套的话,优先级高的 layer ,无论是否有嵌套,都比优先级低的 layer 高,比如:
HTML
<div>I'm using @layers.</div>
CSS
div {
padding: 20px;
width: 300px;
height: 50px;
}
@layer A {
div {
background: blue;
}
@layer B {
div {
background: red;
}
}
}
@layer C {
div {
background: bisque;
}
@layer D {
div {
background: green;
}
}
}
优先级从高到低:@layer C > @layer C.D > @layer A > @layerA.B,所以最后div的背景色为:
@layer 还是一个实验中的功能,我是在 Chrome 100 版本里使用的该特性,Chrome 99、Edge 99、Firefox 97以及 Safari 15.4之后的版本,目前都支持该特性。
浏览器支持情况
accent-color
介绍
accent-color属性可以在不改变浏览器默认表单组件基本样式的前提下重置表单组件的颜色。目前支持的HTML元素有:
<input type=”checkbox”><input type=”radio”><input type=”range”><progress>
accent-color 属性具有继承性,只需要在对应表单控件元素的祖先元素上设置,响应的控件的颜色就会发生变化。
浏览器支持情况
伪类选择器
:is() 和 :where()
:is() 和 :where() 伪类选择器的出现,将会让我们的选择器变得更简洁,比如下成这个 :is() 的示例:
// Level 0
h1 {
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 0
h1 {
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;
}
:where() 和 :is() 的不同之处在于,:where() 的优先级总是为 0 ,但是 :is() 的优先级是由它的选择器列表中优先级最高的选择器决定的。
:not() 和 :has()
或许大家平时在开发前端页面的时候,碰到类似下图这样的需求:
希望最后一张卡片没有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
}
:has()选择器也可以叫做父类选择器,它接受一个选择器组作为参数。有了它,我们可以给有匹配子元素的父类应用一些样式。
<section><!-- section 边框颜色是 blue,margin-bottom是 30px -->
<h1>H1 Level Title</h1>
</section>
<section><!-- section 边框颜色是 #09f,margin-bottom是 30px -->
<h2>H2 Level Title</h2>
</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()的浏览器中,你将看到下图这样的效果:
示例中还演示了
:not() 和 :has() 组合在一起使用的,但两者组合在一起所达到的意思却完全不一样。其中 :not(:has(selector)) 匹配不含有 selector 元素的父元素,而 :has(:not(selector)) 匹配含有的不是 selector 子元素的元素。
:empty 和 :blank
在现代 Web 的开发的过程中,总是无法避免数据吐出为空的情况,此时往往会给我们的 UI 带来额外的麻烦。比如说,在同一类的 UI 元件中编写了一定的样式规则,但数据为空,此时在页面上可能会出现这样的场景:
CSS 的
:empty 和 :blank 两个伪类选择器可以帮助我们避免这种现象。这两个选择器都很有用:
- 给空元素添加样式
- 创建空的状态
:empty 和 :blank的差异:empty要求:空元素是其中没有任何元素的元素。 它甚至不能有空格。只不过:blank还没有得到浏览器的支持。
empty浏览器支持情况
CSS 背景
CSS 的 background 属性是一个简写属性,这个对于大家来说再熟悉不过了。不过在这里着重想把 background-position 和 background-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);
}
background-repeat
在 background-repeat 属性上除了可以使用我们熟悉的 no-repeat 和 repeat (或者 repeat-x 和 repeat-y) 之外还可以使用 round 和 space 。
我们都知道,使用 repeat 的时候,有可能会造成背景图片在平铺的时候被裁剪。如果希望背景图片在平铺时不被剪切,那么可以使用 round 来替代,它的最大特色就是背景图片在平铺的时候会根据容器的宽高对背景图片做相应的尺寸调整。而 space 会留出相应的空间,即在保证背景图片不被裁剪的情况之下,对多出来的空间以空白的方式在背景图片之间留出。
CSS 蒙层和剪切
如果你对设计或设计软件较为熟悉的话,对于蒙层和剪切不会感到陌生。设计师在做一些设计稿的时候,时常也会用到蒙层和剪切的能力。随着 CSS 的发展,在 CSS 的世界中也有了这两个特性,它们在 W3C 的 《CSS Masking Module Level 1》规范中定义,主要的作用如下图所示:
可以灵活的控制内容的显示区域。
蒙层和剪切对应的 CSS 属性就是 mask 和 clip-path ,其中 mask 是一个简写属性,它的使用规则和 background 非常的相似。我将通过简单的示例来向大家展示它们能帮我们做些什么。
先看 mask :
所谓遮罩(mask-image),就是原始图片只显示遮罩图片非透明的部分,在PS中称为“蒙版”。类似于现实世界中一张A4卡纸剪了个洞,我们可以通过洞看卡纸后面的物体,这里卡纸相当于遮罩层,只不过洞是遮罩层不透明的部分,其他部分是遮罩层透明的部分,与我们想象中的正好相反。
上面提到的图片也可以是各种渐变背景色。
h1 {
mask-image: url(mask.png);
}
示例中的 emoji 和文本残缺不全(看上去被啃了一样)。在没有mask 的能力之前,如果我们要实现这样的效果几乎是不太可能,现在有了之后,实现起来就非常的简单。我们只需要像下面这样的一张图片(用于mask-image 上的图片),即蒙层图:
再来看剪切。 clip-path属性可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的URL定义的路径或者外部svg的路径,或者作为一个形状。clip-path属性代替了现在已经弃用的剪切 clip属性。
比如现实需求中,需要实现一些不规则,又是多状态下的UI效果,那么clip-path就方便很多:
属性:
inset(): 定义一个矩形
//语法
inset( <length-percentage>{1,4} [ round <border-radius> ]? )
//说明
inset()可以传入5个参数,分别对应top,right,bottom,left的裁剪位置,round radius(可选,圆角)
//示例
clip-path: inset(2em 3em 2em 1em round 2em);
circle(): 定义一个圆
//语法
circle( [ <shape-radius> ]? [ at <position> ]? )
//说明
circle()可以传人2个可选参数;
1. 圆的半径,默认元素宽高中短的那个为直径,支持百分比
2. 圆心位置,默认为元素中心点
//示例
clip-path: circle(30% at 150px 120px);
ellipse(): 定义一个椭圆
//语法
ellipse( [ <shape-radius>{2} ]? [ at <position> ]? )
//说明
ellipse()可以传人3个可选参数;
1. 椭圆的X轴半径,默认是宽度的一半,支持百分比
2. 椭圆的Y轴半径,默认是高度的一半,支持百分比
3. 椭圆中心位置,默认是元素的中心点
//示例
clip-path: ellipse(45% 30% at 50% 50%);
polygon(): 定义一个多边形
//语法
polygon( <fill-rule>? , [ <length-percentage> <length-percentage> ]# )
//说明
<fill-rule>可选,表示填充规则用来确定该多边形的内部。可能的值有nonzero和evenodd,默认值是nonzero
后面的每对参数表示多边形的顶点坐标(X,Y),也就是连接点
//顺序 顺时针方向
//示例
clip-path: polygon(50% 0,100% 50%,0 100%);
参考: