前端开发者学堂 - fedev.cn

2022 年的 CSS

发布于 大漠

新的一年马上就要开始了!完成文章已经开始了!

虽然近些年 CSS 变化很快,但我认为 2021 年是 CSS 的元年。在即将过去的 2021 年,CSS 变化非常地大,其中新增了很多特性,比如 CSS 容器查询CSS 父选择器CSS 层叠控制规则CSS 子网格等等。而且这些特性已经在个别,甚至是在大部分主流浏览器已经可以看到了。 ​ 几大主流浏览器(Chrome、Firefox、Safari和Edge)还专注于修复浏览器兼容性痛点,让 Web 开发者的工作变得更轻松。 ​

浏览器兼容性方面的修复,在 Web.dev 上发布了 2021 年的年中年终报告,报告虽然简单,但我们可以看到浏览器厂商都在致于力磨平 CSS 特性在浏览器上的兼容性。为此,我们应该感谢他们的付出!

随着 2021 年的结束,让我们一起来看看 2022 年,我们可以期待哪些 CSS 特性将会在浏览器中出现。这了是我连续第三年整理有关于 CSS 新特性方面的文章了,另外两篇是:

你即将看到是 2022 年的,你会发现这三篇文章提到的 CSS 特性有相同的,但他们也是有变化的。 ​

回顾近两年的 CSS 发展

@Bramus 前两天在Twitter 上发了一条信息,他整理了一份 2022 年的将会出现在浏览器的 CSS 清单: ​

有关于清单中列出的 CSS 相关介绍,可以阅读他的文章:《CSS In 2022

这份清单中列出的 CSS 特性和 @Adam Argyle 分别在 2020 年 和 2021 年分享的 CSS 新特性列的出的清单差不多,不同的是,在2021年,有些CSS特性已经得到了浏览器的支持,有些已经进入了浏览器的实验属性列表清单,说不定2022年,你就能看到了!

有关于 @Adam Argyle 在 2020 和 2021 分享的 CSS 新特性,我也曾整理过一份,清单有些类似,但内容还是有差异,主要涵盖了一些我自己在 CSS 方面的认识和尝试,分别记录在《2020年CSS有哪些新特性》和《2021年你可能不知道的 CSS 特性》以及《应用于下一代Web的样式

当然,如果你也是 CSS 的忠实爱好者,不断的在关注和推进 CSS 向前发展,那么你可能已经知道了,自 2019 年开始,每年年低都会有一份关于 CSS 发展状态相关的报告(201920202021)呈现给你。在每年的报告中,我们都可以看到一项关于”开发者期待的CSS特性“的统计:

在第一份报告出来的时候,我特意还花了一点时间去理解这份有关于 CSS的报告:《从9102年的CSS状态报告中看CSS特性的使用》,另外有幸运在 2020年年底和一些大学生一起聊了一个关于CSS状态和学习的话题!如果你对该话题感兴趣的话,可以阅读《CSS现状和如何学习》,文章中附有分享时录制的视频!

除此之外,W3C 的 CSS 工作小组,这两年也都出了 CSS 规范的重点报告,最新的是 2020年 和 2021年的。注意,W3C 关于 CSS 规范的重点报告并不是每年都有的! ​ 接下来,我将按着 @Bramus 的 《CSS In 2022》大纲往下梳理,其中会加入一些我自己对这方面的认识和见解。

image.png

我需要额外提出的是,接下来内容中提到的 CSS 特性只会涉及到那些全新的或仍然没有得到浏览器支持的(哪怕是实验性方面的)特性。但这并不代表着它们不值得期待,说不定在2022年你就能使用了!如果你对这方面感兴趣的话,请继续往下阅读! ​

热门的 CSS 特性

在这节中提到的 CSS 特性是近些年来,Web 开发者一直期待的特性(或者说 CSS 中遗失的特性)。这些特性将在 2022 年的某个时候就得到了主流浏览器的支持。庆幸的是,有些特性已经在一个或多个主流浏览器中得到了支持,其他特性也将随着时间的推移也会随之而来。在 2022 年,学习或了解这些 CSS 特性,我想你会获得收益的! ​

有意思的是,CSSWG一直还维护着一份关于 CSS 设计中不完整的错语列表。感兴趣的可以点击这里阅读

CSS 容器查询

CSS 容器查询的特性一直以来都是 Web 设计师和开发者所期待的功能之一,从这几年的 CSS 发展状态的报告就可以看得出来。在没有容器查询之前,Web 开发者大多是依赖于 JavaScript 脚本来做判断,从而改变 UI 网格。 ​ 虽然 CSS 容器查询存在于 CSS Containment Module 中有些年头,但一直未得到浏览器的支持。庆幸的是,在 2021 年,该特性得到了飞速的发展,时至今日,能在主流浏览器中看到 CSS 容器查询功能。这一切,都离不开 @TerribleMia ,是她为我们带来了这么好的 CSS 特性,并且一直在推进该特性向前发展。 ​

@TerribleMia 除了设计 CSS 容器查询特性之外,还设计了 CSS 另外两个非常优秀的特性,那就是 CSS 的 @layer@scoped 两个 @ (At rule)规则。详细的可以阅读《Miriam's CSS Sandbox》。

CSS 容器查询 @container 有点类似于 CSS 的媒体查询 @media ,只是它将根据元素的父容器(或祖先元素)的尺寸(size)或样式(style)来调整自己或自己后代元素的样式规则。在没有 CSS 容器查询,Web 开发者为了能在不同容器下调整 UI,大多都是依赖于媒体查询来做。也就是说,有了该特性之后,不需要再依赖视窗大小加添加类名的方式来调整UI了: ​

上图演示了,基于视窗的设计和基于容器的尺寸设计。可以说, CSS 容器查询的出现,除了改变 Web 开发者的开发方式之外,也将给 Web 设计带来变革命。这个论点在 2021 年的 GDS 大会上,@Una Kravets 在分享的 《The New Responsive: Web design in a compoent-driven world》主题(该主题视频可以在 YouTube上访问)中就提出这样的观点: ​

CSS 容器查询将会成为下一代响应式 Web 设计必不可少的特性之一。

有了 CSS 容器查询之后,我们就可以基于同一个组件,在不同尺寸下调整其UI,比如我们手淘 APP 顶部的搜索框效果: ​

上面效果的关键性代码如下:

.form__container {
    container: inline-size;
}

.form {
    display: grid;
    align-items: center;
}

@container (min-width: 480px) {
    .form {
        grid-template-columns: min-content 1fr 200px;
        grid-template-areas: "searchIcon searchInput button";
        grid-template-rows: 88px;
        gap: 10px;
    }
}

@container (min-width: 768px) {
    .form {
        grid-template-columns: min-content 1fr min-content 200px;
        grid-template-areas: "searchIcon searchInput cameraIcon button";
        grid-template-rows: 88px;
        gap: 10px;
    }
}

详细代码可以点击这里阅读或获取

CSS 容器查询在主流浏览器都能体验效果了,但需要开启其相关标记。如果你要运用于生产环境,可以考虑其相应的 Polyfill: ​

CSS容器查询特性绝对是众望所归的一个特性,在2022年更会全速向前!

​有关于 CSS 容器查询更多的介绍可以移步阅读下面这些文章:

CSS的父选择器 :has()

CSS 选择器是 CSS 中最基础的知识,也是必不可少的一部分,如果你没掌握好 CSS 选择器的话,那么你将会在 CSS 的世界中有那种”众里寻她千百度,蓦然回首,那人却在灯火阑珊处“的感觉!也正因此,CSS 选择器模块的迭代非常的快,现在已经进入 Level 4 版本了。 ​ 在 Level 4 版本中,添加了多个伪类选择器,比如 :is():where():not():has() 等,而其中 :has() 选择器也是我们期待已久的一个选择器。@SaraSoueidan 在 Twitter 上引用 @Jen Simmons 的话说: ​

:has()选择器本质上就是CSS中期待已久的父选择器!

CSS 的 :has() 伪类选择器和 :not() 有点相似,也被称为结构性伪类选择器,在 CSS 的函数中也称之为 动态伪类函数。它允许你更精细地匹配元素: ​

:has() 伪类代表一个元素,如果作为参数传递的任何选择器至少与一个元素相匹配!

简单地说,元素只有在传递到 :has() 中的选择器至少匹配一个元素时才会被选中。这样理解起来似乎有点晕,我们来看个简单地示例:

figure img { 
    aspect-ratio: 21 / 9; 
    border: 5px solid #3f51b5; 
}

figure:has(figcaption) img { 
    border: 5px solid #9c27b0; 
}

上面示例中的 figure img 选择器大家应该都明白,表示选中 <figure> 元素中的所有 <img> 元素;而 figure:has(figcaption) img 选择器表示的是选中 包含了<figcaption> 元素的 <figure> 元素中的所有 <img> 元素。注意,这里:has() 中传了个 figcaption 选择器作为其参数。

<!-- 未匹配,因为 figure 没有包含 figcaption 元素 -->
<figure>
    <img alt="" src="" />
</figure>

<!-- 会匹配,因为 figure 包含了 figcaption 元素 -->
<figure>
    <figcaption></figcaption>
    <img alt="" src="" />
</figure>

在支持的浏览器中你将看到的效果如下: ​

注意,Safari TP 137 是目前唯一一个默认支持 :has() 选择器的浏览器。

虽然说 :has() 被称为 CSS 的父选择器,但它的作用远不止于此。我们可以使用 :has():not() 等选择器相互结合,实现一些更复杂的效果。 ​

就上面示例而言,当你在 <input type="email"> 输入的值是否有效时,表单不同状态下有不同的 UI 效果:​

是不是很有意思。如果对 CSS 的 :has() 选择器感兴趣的话,还可以阅读《CSS 选择器:is():where():has() 有什么功能》一文。

更多关于选择器的内容:

@layer 控制 CSS 的级联层

CSS的级联顺序一直以来令众多开发者(特别是不熟悉CSS的开发者)感到困惑和头痛。CSS 新的 @ 规则 @layer 将可以让 CSS 的级联顺序按照你的意图来进行控制。简单地说,@layer 可以通过分层的方式,让你适当控制同源规则的级联排序。

比如下面这个示例:

/* 预设级联层的顺序,并且相邻级联层之间有逗号分隔 */ 
@layer setting, tool, generic, element, object, component, utilities;

@layer setting { 
    /* 附加到级联层 setting 中的 CSS */ 
} 

@layer tool { 
    /* 附加到级联层 tool 中的 CSS */ 
} 

@layer generic { 
    /* 附加到级联层 generic 中的 CSS */ 
} 

@layer element { 
    /* 附加到级联层 element 中的 CSS */ 
} 

@layer object { 
    /* 附加到级联层 object 中的 CSS */ 
} 

@layer component { 
    /* 附加到级联层 component 中的 CSS */ 
} 

@layer utilities { 
    /* 附加到级联层 utilities 中的 CSS */ 
}

上面只是演示了 @layer 规则的一个基本使用,而与 @layer 相关的知识和细节很多,这里就不深入展开了。要是你对该特性感兴趣的话,可以阅读《初探 CSS 的级联层(@layer》一文。 ​ 到目前为止,你们可以在 Chromium 99、Firefox 97 和 Safari TP 133 中查阅。注意,Safari 是最早支持该特性的浏览器。

有关于 CSS 中层叠更多的话题还可以阅读:

颜色函数

CSS Color Module Level 5 中新增了两个处理颜色的新函数,即 color-mix()color-contrast() ,除此之外,还扩展了以前的颜色函数(比如 rgb()hsl()hwb()lab()lch() 等)功能,可以在一个颜色的基础上改变某一个或某几个参数的值,从而得到一个新的颜色。 ​ 比如上图部的 hsl() 函数,我们可以基于 --theme-primary 颜色(假设它的值为 hsl(274, 61%, 50%))改变其饱和度(从 50% 调整到 30% )从而得到一个新的颜色 hsl(274, 61%, 30%) 。这个特性,可以让我们在很容易的在 CSS 控制品牌色的色盘: ​

说个题外话,我最近的主要工作内容之一是将 Design Token 运用于前端生产的工程链路中,并且根据组件可变参数来自动生成不同风格的 UI 组件。那么,在未来的某一天,我就可以使用这种特性来为品牌色构建色盘。

新增的 color-mix()color-contrast() 函数相对来说要更复杂一些。其中 color-mix() 函数有点类似于设计师调色一样:允许你在一个给定的颜色空间中混合两种颜色。 ​

比如:

:root {
    --theme-color: #ff0000;
}

.text-primary-dark {
    color: color-mix(var(--theme-primary), black 10%);
}

.text-primary-darker {
    color: color-mix(var(--theme-primary), black 20%);
}

color-contrast() 函数比较有意思,特别是在用于构建可访问性 Web 的时候特别有用。因为它可以帮助我们提高 Web 可访问性方面的能力(更好的控制文本色和背景色的对比度)。其主要作用是获取一个颜色值,并将其与其他颜色的列表进行比较,从列表中选择对比度最高的一个。 ​

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

你也还可以像这样使用:color-contrast(wheat vs tan, sienna, #d2691e to AA-large) 。它会将 wheattansienna#d2691e 进行对比,最终 sienna 颜色获胜,因为它与 wheat 颜色的对比度为 4.273 ,超过了 AA-large 的阈值。 ​

Safari TP 124 已将 hwb()lch()lab()color-mix()color-contrast() 置为默认功能!

​额外提一下,其中 hwb()lch()lab() 所表达的颜色空间和 rgb() 还是有差异的,它们能将颜色表达的更细腻。就拿 hwb() 函数来说: ​

基于同一色相 0deg 描述的颜色: ​

有关于 CSS 颜色更多介绍,可以阅读:

视窗单位

”视窗单位“对于大家来说应该不会感到陌生了,特别是针对移动端开发的同学来说。因为移动端现在主流的适配方案之一就是采用视窗单位来处理的。但很多同学所知道的视窗单位应该只是 vwvhvminvmax : ​

视窗单位给移动端开发的适配是带来了极大的优势,但我想你在使用视窗单位的时候,应该也碰到了iOS上 Safari 的兼容性问题。因为,在iOS上的 Safari 有一个长期存在的,极其恼人的Bug,它不能与 vh 单位很好的配合。如果你将一个容器的高度设置为 100vh 时,会导致这个元素有点太高(会出现滚动条)。造成这种现象的主要原因是移动端上的 Safari 在计算 100vh 时忽略了它的部分用户界面。

如果你对 iOS 的 Safari上 100vh 的相关问题以及解决方案感兴趣的话,还可以移步阅读:

如果你不想花过多时间搞清楚这个问题的话,只是想快速解决这个问题,那可以将下面这段代码放到你的代码片段中:​

body {
    height: 100vh;
}

@supports (-webkit-touch-callout: none) {
    body {
        height: -webkit-fill-available;
    }
}

虽然上面的代码可以解决 100vh 在 iOS Safari 引起的问题,但终究也只能说是一种 Hack 手段(事实上,CSS 中有很多黑魔法,这里不是主要聊这个的,顠过)。 ​ 庆幸的是,CSS Values and Units Module Level 4 新增了几个与视窗有关系的新单位: ​

  • svh/svw:小视窗高度(height)、宽度(width)的 1%
  • lvh/lvw:大视窗高度(height)、宽度(width)的 1%
  • dvh/dwv:动态视窗高度(height)、宽度(width)的 1%

也有相应的单位用于 CSS 的逻辑属性中,比如 svi/svb 。有关于 这几个新增的视窗单位更详细的介绍,可以阅读 @Bramus 的 《The large, Small, and Dynamic Viewports》一文和@Arek Nawo 的 《Investigating the new CSS viewport relative units》一文。 ​ 既然聊起了 CSS 的单位,那就再多花点篇幅和大家再多聊几个新增的 CSS 单位,也是蛮有意思的。先说 CSS Values and Units Module Level 4 新增的 lhrlh 吧。

简单地回忆一下 CSS 中另一对单位:emrem 。稍微了解 CSS 的同学都知道: ​

  • em 是相对于元素自已的 font-size 值计算(除元素自身的 font-size 取值为em时,元素自身的 font-size 值单位为 em时,其相对于其父元素的font-size值计算)
  • rem 是相对于HTML文档的根元素的 font-size 值计算,文档的根元素一般是指 <html> 元素

这两个新增的 lhrlhemrem 非常的相似,只不过他们相对的是 line-height 的值计算: ​

  • lh 相对于元素自己的 line-height 计算
  • rlh 相对于文档根元素(<html> )的 line-height 计算

你肯定会问,这样的单位有何用处呢?我想大家在还原 UI 设计稿的时候,肯定碰到了因为字体不同以及 line-height 值不同,让元素在视觉上看上去总是对不齐。那么,有了 lhrlh 之后,事情会变得好一点。比如下图这样的场景: ​

以前上图这样的标记,常把宽高设置为 1em ,那么现在可以设置 1lh

::marker {
    width: 1lh;
    height: 1lh;
}

到目前为止,仅 Safari TP 105 声称支持这两个相对单位 lhrlh 。 ​ 前面提到 CSS 容器查询特性一直以来是 Web 设计师和开发者一直期待的特性,并且在 2021 年得到快速发展。同时,和 CSS 容器查询特性一起出现的 ”*容器查询单位“ 也是很有意义和作用的。只不过,CSS 容器查询单位与 CSS 容器查询模块放在一起,并未和其他的 CSS 单位纳入一起。 ​ CSS 容器查询单位和视窗单位有点类似,不同的是 视窗单位相对于浏览器视窗尺寸计算,而容器查询单位相对于查询容器计算。使用容器查询长度单位的样式表可以更容易将一个组件从一个查询容器中移到另一个查询容器。CSS容器查询的单位主要有:

  • cqw:等于查询容器宽度(width)的 1%
  • cqh:等于查询容器高度(height)的 1%
  • cqi:等于查询容器内联尺寸(inline-size)的 1%
  • cqb:等于查询容器块尺寸(block-size)的 1%
  • cqmincqicqb 中较小的那个(也可以是 cqwcqh 中较小的那个)
  • cqmaxcqicqb 中较大的那个(也可以是 cqwcqh 中较大的那个)

早其容器查询的单位定义的是 c*(比如,cwchcmincmax )之类的,但其中有些单位会和 ch 单位产生冲突,因此,最终确定的容器查询单位是以 cq* 开始的。也就是上面所列的几个!

正如 @Miriam Suzanne(最初提出建议的人,也是规范的编辑)所分享的,这些 CSS 单位是Chromium中实验性容器查询支持的一部分。 ​

更为有趣的是,@Ahmad Shadeed 在他的文章《CSS Container Query Units》一文中提出 qwqhqminqmax 以及他们对应的逻辑属性的单位qiqb

@Ahmad Shadeed 使用这些新的CSS单位运用在 font-size 上。可以用于容器查询的一坨 CSS 代码,clamp() 比较函数中使用cqw 单位来取代。

过度滚动行为:overscroll-behavior

早在2018年和大家聊改变滚动体验的时候就介绍过 overscroll-behavior 属性,我们可以通过该属性覆盖 overscroll 容器(指的是内容宽或高大于容器的宽或高,出现滚动条)时的默认行为。拿一个具体的实例来说,比如我们在构建弹框的时候,弹框内容过高也会出现滚动条,这个时候就会有两个滚动条出现,一个是弹框的,一个是body 的,其默认行为会像下面录屏的效果: ​

弹框无法滚动时,其底部的内容可以继续滚动。说实话,这样的默认滚动行为给用户的体验是极差的。更多的时候,我们希望弹框无法滚动时,其底部的滚动条也不能滚动。如下所示:

要实现上面视频的效果,我们就需要使用 overscroll-behavior 属性,并且将其值设置为 contain :

.modal {
    overscroll-behavior-y: contain;
    overflow-y: auto;
}

这个特性早在 Firefox 36 和 Chrome 63 就开始得到支持了,只不过Safari还未得到支持,不过 Safari 也在迎头赶上。 ​

有关于 overscroll-behavior 的相关介绍,可以阅读 @Ahmad Shadeed 的《Prevent Scroll Chaining With Overscroll Behavior》一文。

在 CSS 中改善滚动体验,除了overscroll-behavior 属性之外,还有其他的一些属性,比如滚动捕捉 scroll-snap (Scroll Snap模块中的属性)、pull-to-refresh 等。 ​ CSS 除了要以控制滚动行为之外,也可以控制滚动条的样式。在今年以前,CSS 控制滚动条样式是使用浏览器的一些私有属性来搞定: ​

就在 2021 年 12 月 09日,W3C 发布了的 CSS Scrollbars Styling Module Level 1 规范,该规范提供了 scrollbar-colorscrollbar-width 两个属性,用来给滚动条设置样式。以后,我们要以使用它们来轻易完成滚动条样式的定制:

.section {
    scrollbar-color: #6969dd #e0e0e0;
    scrollbar-width: thin;
}

我想大家都知道,使用 CSS 来美化滚动条样式主要原因之一是因为滚动条在不同的系统平台上显示有差异,外观不统一。除此之外,还有另一个问题。在 Web 上显示滚动条有一个副作用,那就是 内容的布局可能会根据滚动条的类型而改变。如果我们想防止由滚动条引起的一些不必要的布局变化,希望有相应的 CSS 属性来处理。 ​ 以前没有,但现在有了。CSS Overflow Module Level 4 新增了一个 scrollbar-gutter 属性,有了这个属性,开发者就可以更好的控制滚动条,或者说解决因滚动条类型不同引起布局的差异变化。下图中展示了 scrollbar-gutter 的取值不同时的效果:

如果你对美化滚动条以及 scrollbar-gutter相关的知识感兴趣的话,可以阅读:

CSS网格的 subgrid

Web 布局 而言,虽然 Flexbox 很优秀了,但在二维布局中 Flexbox 还是有很大的局限性。在整个 Web 布局的技术体系中,只有 CSS Grid 才是唯一的二维布局。 ​ CSS Grd 布局在这几年中一直在不断的向前推进,已经得到了主流浏览器的支持。CSS Grid 能这么快向前推进,除了要感谢浏览器厂商的开发者团队之外,我个人认为还必须要感谢 @Jen Simmons@Rachel Andrew。 ​

@Jen Simmons@Rachel Andrew 除了是 CSS Grid 规范的缔造者,还是 CSS Grid 布道者,她一直在社区努力推进 Grid 向前发展。

CSS Grid 已经有很多年了,该模块中的很多特性在主流浏览器中都得到了支持。在 2021 年,我自已陆续花了近半年的时间,对 CSS Grid 技术进行了系统的学习,并且整理了二十多篇有关于 CSS Grid 方面的系列教程,在这个系列中除了 CSS Grid 的理论知识之外,还有一些实战案例。要是你对这方面感兴趣的话,可以从《2022年不能再错过 CSS 网格布局了》一文中索引。 ​ 这里着重把 CSS 网格中的子网格 subgrid 单独拿出来是原因的。子网格从定义到今天,经历了很多个版本的演变,而且在 CSS 社区也引起来很大的争议。在 CSS Grid 出现之后,就有人提出: ​

在 CSS 网格布局系统中应该要有一个子网格,即 subgrid

为此,最早定义的 subgridgridinline-grid 一样,它只是 display 属性的一个值:

.subgrid {
    display: subgrid;
}

不过没过多年,subgrid 就从 display 属性中移除了。使用嵌套网格来模拟子网格: ​ .grid { display: grid; grid-template-columns: repeat(4, 1fr); }

.grid__item {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
}

简单地说,在需要嵌套网格的网格项目上再次使用 display: grid 以及 grid 相关的属性重新定义一个网格。只不过,这种方式也问题存在:很难将嵌套网格项目与父网格对齐。换句话说,父网格和嵌套网格是两个独立的网格,他们有着自己独立的网格参数。 ​ 为了让子网格能继续父网格的相关参数,才又将 subgrid 再次引入到 CSS Grid 系统中,只不过,subgrid 不再是 display 的值,而是网格属性 grid-template-columnsgrid-template-rows 属性的值。

.grid__container { 
    display: grid; 
    grid-template-columns: 1fr 2fr 3fr 2fr 1fr; 
    grid-template-rows: 1fr 2fr 2fr 1fr; gap: 1rem; 
} 

.grid__container--subgrid { 
    grid-column: 2 / 5; 
    grid-row: 2 / 4; 
} 

.grid__container--subgrid { 
    display: inherit; 
    grid-template-columns: subgrid; 
    grid-template-rows: subgrid; 
}​

正如上图所示,这就是subgrid的作用:通过设置grid-template-columnsgrid-template-rowssubgrid,它将与父网格对齐。真正的子网格可以使用父网格的相关参数。简单地说,使用该特性,可以轻易实现像下图这样的卡片布局: ​

还有一段时间,也有另一种建议,那就是使用 display: contents 来替代 subgrid 。而到今天为止,在网格布局系统中,嵌套网格、子网格和 display: contents 模拟的子网格都同时存在。他们都有着自己的特性。这里就不花过多时间阐述了。 ​ subgrid已成为 CSS Grid 模块中不可或缺的一个特性,直到今天为止(2012年的最后一天),也仅 Firefox 浏览器支持。不过,Chrome 已进入 WIP 阶段,我想 2022 年上半年,你就能在 Chrome 浏览器体验 subgrid 的效果。

既然提到 CSS Grid,那就顺嘴说一下 CSS 的 gap 属性吧。该属性不只是 Grid 布局独有的,在 CSS Flexbox 、Grid 和多列布局中都有 gap 属性。在布局中,在某些场景中,给相邻元素之间设置间距要比 margin 容易地多。 ​

@Jen Simmons 发的推特消息中可以获知,Safari 14.1 开始也支持 Flexbox 中的 gap 属性。我自测了一下,Grid中的 gap 也得到支持了。也不是说,现在主流浏览器都已支持 gap 属性了: ​

accent-color

Web 中有很多控件的 UI 效果是跟随系统走的。比如表单中的一些控件,比如常用的输入框(<input>)、单选按钮,复选框,进度条等。 ​

以往,为了在 UI 的风格上能满足设计师的需求,即 所以平台上使用风格一致的UI效果。为此,Web 开发者需要增加额外的开发工作量,采用自定义表单控件的方式来让 UI 网格统一。 ​ 为了让开发者能更好的满足设计师的需求,并且快速让各种平台统一 UI 网格, CSS Basic User Interface Module Level 4 新增了 accent-color 属性,可以很轻易的控制 Web 控件 (Widget Accent) UI:

:root { 
    accent-color: deeppink; 
} 

@media (prefers-color-scheme: dark) { 
    :root { 
        accent-color: hsl(328 100% 65%); 
    }
}

既然提到 Web 表单中的控件,那就给大家提两个有关于 <input> 元素的属性。因为这两个属性能给你的用户带来更好的体验,特别是在操作表单的时候。除了给 <input> 元素的 type 指定不同值时,可以提供不同键盘类型之外,还可以使用 inputmode 属性: ​

inputmode 可以唤起不同类型的软键盘,另一个改善用户体验的是给 <input> 元素的 enterkeyhint 属性设置不同的值,可以改变软键中的 Enter 键的类型的操作行为:

上面提到的,不管是 CSS 还是 HTML 的属性,都是用来改变用户体验的。再提一个和 HTML相关的属性。

自Safari 15 开始,我们在 HTML 的 <meta> 标签为 theme-color 设置颜色,让浏览器自身的 UI 颜色有让开发者来控制: ​

特别声明,这里的 theme-color 不是 CSS 属性,他是 HTML 的 <meta> 元素的 name 的一个值,结合 contentmedia 能轻易控制系统级的颜色。

似乎跑题了!我们要聊的是 2022 年的 CSS !嗯!那我们继续回到 CSS 的世界中来。 ​

媒体查询

CSS 媒体查询 @media 已不是什么新特性了(除了新增的一些用户偏好的条件设置)。但今天要提出的是他的语法规则的写法。先上一张图吧:

以往在 @media 或者在 @container 规则中写判断条件时使用 min-widthmax-width 较多,不知道大家是否和我一样有这样的感觉,使用 min-widthmax-width 很多时候傻傻分不清楚他们的范围。为此,我总是喜欢使用下图来做判断: ​

CSS Media Queries Level 4 开始,我们可以使用较为熟悉的数学表达式了,在媒体条件中使用 >>=<<= 等数学表达式: ​

上图中使用 @media 语法表达的话,像下面这样:

/* Old Way */
@media (max-width: 768px) {
    …
}

@media (min-width: 375px) {
    …
}

@media (min-width: 375px) and (max-width: 768px) {
    ...
}

/* New Way */
@media (width <= 768px) {
    …
}

@media (width >= 375px) {
    …
}

@media (375px <= width <= 768px) {
    ...
}

同样的,数学比较运算符表达式也同样可以运用于 @container 容器查询的条件判断中。

如果你对媒体查询方面的知识感兴趣的话,还可以阅读:

F-mods

F-mods 是 Font Metrics Override Descriptors(字体度量覆盖描述符)的简称。它对应着 CSS Fonts Module Level 4 规范中的 @font-face 部分的第十一小节,即 默认的字体度量覆盖。简单地说,在 @font-face 规则中新增了 ascent-overridedescent-overrideline-gap-overrideadvance-override 等属性:

@font-face { 
    font-family: 'Arial' src: local('Arial'); 
    --unitsPerEm: 1000; 
    --lineGap: 10; 
    --descender: -237; 
    --ascender: 1041; 
    --advanceWidthMax: 815; 
    ascent-override: calc(var(--ascender) / var(--unitsPerEm) * 100%); 
    descent-override: calc(var(--descender) / var(--unitsPerEm) * 100%); 
    line-gap-override: calc(var(--lineGap) / var(--unitsPerEm) * 100%); 
    advance-override: calc(var(--advanceWidthMax) / var(--unitsPerEm)); 
}

其中 advance-override 还未进入 W3C 规范,目前还仅处于提案中;而 ascent-overridedescent-overrideline-gap-override 属性目前已在 Chromium 87+ 和 Firefox 89+ 中实现!

这四个描述符的组合可以让我们告诉浏览器在下载 Web Fonts 之前字符占用多少空间,来覆盖回退字体(系统字体)的布局,使其与 Web Fonts 相匹配。简单地说:这四个描述符可以让你的系统字体更接近 Web Fonts! ​ 其中 ascent-overridedescent-overrideline-gap-override 描述符使我们 能够完全消除垂直布局的偏移,因为这三个描述符都会影响行高。 ​ 在 CSS Fonts Module Level 5 规范中又为 @font-face 规则添加了几个新属性。比如,用来覆盖字体上标(sup)和下标(sub)的 superscript-position-override subscript-position-overridesuperscript-size-overridesubscript-size-override 描述符。虽然这几个属性还没有得到任何浏览器的支持,但对于 Web 开发者而言,这是希望。

除此之外,还新增了 size-adjust 描述符,它允许我们调整字形的比例系数(百分比)。该描述符取代了前面提到的 advance-override描述符。正如 @Addy Osmani在他自己推特上,是这样描述 size-adjust: ​

size-adjust 已经得到了 Chromium 92+ 和 Firefox 89+ 的支持。

说到 @font-face 那就有必要提到大家也一直期待的另一个 CSS 特性,那就是 leading-trim: ​

leading-trim 其主要作用就是用来改变文本布局。

在铅印刷术中,为了让铅字块(排成行)之间有一定的间距,排字工人会在行与行之间垫一些铅条,让阅读变得更舒服一些。在那个时代,锅条一般都放置在铅块下方。同样的,在Web排版上也有铅条的身影,只不过他分面上下两个部分。在CSS中引入该特性,它的工作原理就像一个文本框剪切刀。去掉文本框上下之间多余的空间(这个空间其实就是铅条高度的一半): ​

h1 { 
    text-edge: cap alphabetic; 
    leading-trim: both; 
}

示例中用到了text-edge 属性,简单地用下图来描述其作用: ​

开发 Web 时,对于排版方面是较为复杂的,涉及到的CSS知识也较多,如果你感兴趣的话,可以阅读下面这些文章:

下一代CSS Transform

这个和前面提到的 CSS 媒体查询有点类似。不是 CSS 的新特性,在 CSS Transform Level 2 规范中把以前用到 transform 属性的 rotate()scale()translate() 函数变成独立的 CSS 属性。而且它们都已经成为主流浏览器的实验属性,能在浏览器中体验他们。

element {
    scale: 2;
    rotate: 30deg;
    translate: -50% -50%;
}

平移(translate)、旋转(rotate)和缩放(scale)属性允许开发者独立地指定简单的变换,其方式与典型的用户界面使用方式一致,而不必记住变换中的顺序,使transform()rotate()scale()的动作保持独立并在屏幕坐标中发挥作用。 ​ 它们被应用的顺序是,首先是平移,然后是旋转,然后是缩放,而不是你定义它们的顺序。有了独立的变换属性,这也意味着我们可以对它们分别进行动画和过渡。

@keyframes individual {
    50% {
        translate: 0 50%;
    }
    75% {
        scale: 1;
    }
}

element {
    transition:  rotate 200ms ease-in-out, scale 500ms linear;
}

element:hover {
    scale: 2;
    rotate: -3deg;
}

有关于这方面更详细的介绍可以阅读:

实验性属性

接下来提到的 CSS 特性只是部分浏览器的实验性功能,不会得到大多数浏览器的支持,或者可能只有在相应的浏览器中开启了实验性功能标记才能看到效果。这些特性在 2022 年,甚至是再往后一两年都有可能得不到所有主流浏览器的支持。除了不会得到浏览器支持,而且规范都有可能会变更。说不定还会从 CSS 中移除! ​

CSS 的嵌套

上图看上去很熟,对吧!但它不是 SCSS 中的嵌套,是原生 CSS 中的嵌套。在 CSS 方面这算是一个突破性的补充,也算是继续 CSS 自定义属性(CSS 变量)的又补充。以往这样的规则,只能在 CSS 的处理器中运行。 ​ 在 CSS 的嵌套模块中,我们可以使用 &@nest 规则在一个样式规则块中嵌套另一个样式规则块:

table.colortable {
    & td {
        text-align: center;
        &.c { text-transform: uppercase }
        &:first-child, &:first-child + td { border: 1px solid black }
    }
    & th {
        text-align: center;
        background: black;
        color: white;
    }
    @nest footer & {
        font-size: 0.8em;
    }
}

有关于 CSS 的原生嵌套更多的介绍,可以阅读《图解CSS:CSS的嵌套》一文。

CSS作用域:@scope

CSS Cascading and Inheritance Level 5 中引入了 @layer 规则,将在 Level 6 中会引入另一个规则 @scope 。这是一种将样式范围扩大到DOM树的一种用法。

<!-- HTML -->
<div class="dark-theme">
    <a href="#">plum</a>
    <div class="light-theme">
        <a href="#">also plum???</a>
    </div>
</div>

/* 当.light-theme和.dark-theme被嵌套时,你可能不会得到预期的结果 */
.light-theme a { 
    color: purple; 
}

.dark-theme a { 
    color: plum; 
}

/* 通过范围划分,我们可以解决这个问题,因为a元素的样式将由其最近的范围来确定 */
@scope (.light-scheme) {
    a { 
        color: darkmagenta; 
    }
}

@scope (.dark-scheme) {
    a { 
        color: plum; 
    }
}

@when ... @else

@when ... @elseCSS Conditional Rules Module Level 5 新增的特性,他们可以分开来单独使用,也可以组合在一起使用。

@when media(width >= 400px) and media(pointer: fine) and supports(display: flex) {
    /* A */
} @else supports(caret-color: pink) and supports(background: double-rainbow()) {
    /* B */
} @else {
    /* C */
}

@Stefan Judis 将这个CSS 特性(@when ... @else)有前面提到的 CSS特性,即 CSS 容器查询 和 媒体查询的数学运算表达式组合在一起,重新设计了 @Ahmad Shadeed 的 《Conditional Border Radius In CSS》文章中提到的有条件设置圆角:

@Stefan Judis 重新设计的方案:

有关于有条件设置圆角半径除了这里提到的方案,其实还有其他的方案,你要是想一探究竟的话,可以阅读前几天刚整理的《CSS 中的条件圆角技巧》一文。

滚动与动画

这里所说的滚动与动画指 Scroll-linked Animations 模块中提供的 @scroll-timeline规则和 animation-timeline 属性。你可以将 CSS 动画与一个滚动容器的滚动偏移量关联起来。当你向上或向下滚动一个容器时,链接的动画将相应的前进或后退。

比如像下面效果:

使用 @scroll-timeline 规则和 animation-timeline 构建类似上面的效果,要以按下面三步来走:

/* (1) Define Keyframes */
@keyframes adjust-progressbar {
    from {
        transform: scaleX(0);
    }
    to {
        transform: scaleX(1);
    }
}

/* (2) Define a ScrollTimeline */
@scroll-timeline scroll-in-document {
    source: auto;
    orientation: block;
    scroll-offsets: 0, 100%;
}

/* (3) Attach the Animation + set the ScrollTimeline as the driver for the Animation */
#progressbar {
    animation: 1s linear forwards adjust-progressbar;
    animation-timeline: scroll-in-document;
}

有关于这方面更详细的介绍,可以阅读 @Bramus 提供的系列教程:

CSS Houdini 变量:@property

@property 是用来注册一个变量的,该变量是一个 CSS Houdini 中的变量,但它的使用和 CSS 中的自定义属性(CSS变量)是一样的,不同的是注册方式:

// Chrome 78+
// 需要在 JavaScript脚本中注册
CSS.registerProperty({
    'name': '--custom-property-name',
    'syntax': '<color>',
    'initialValue': 'black',
    'inherits': false
})

// Chrome 85+
// 在CSS文件中注册
@property --custom-property-name {
    'syntax': '<color>',
    'initialValue': 'black',
    'inherits': false
}

他的最大特色之一就是可以指定已注册的 CSS 变量的类型、初始值,是否可继承: ​ ​

虽然它的使用方式和 CSS 的自定义属性相似,但它要更强大,特别是在动效方面的使用,能增强 CSS 的动效能力,甚至实现一些以前 CSS 无法实现的动效。比如

@property --milliseconds {
    syntax: '<integer>';
    initial-value: 0;
    inherits: false;
}
.counter {
    counter-reset: ms var(--milliseconds);
    animation: count 1s steps(100) infinite;
}

@keyframes count { 
    to {
        --milliseconds: 100;
    }
}

把它和 CSS Houdini 的 Paint API 结合起来,可做的事情更多: ​ ​

更多这方向的效果可以在 houdini.how 网站上查阅:

CSS Houdini 中的 @property 和 Paint API 是对现在 CSS 能力的扩展,如果你对这方面感兴趣的话,可以阅读:

瀑布流布局

瀑布流是 Web 中经典布局之一。到目前为止依旧是 JavaScript 实现的瀑布流布局为主要技术方案,虽然使用 CSS 技术在一定条件之下可以完成瀑布流布局,但其局限性也较大。 ​ 在 CSS Grid 最新模块中提供了原生的 CSS 瀑布流布局方案

.masonry { 
    display: grid; gap: 20px; 
    grid: masonry / repeat(auto-fill, minmax(250px, 1fr)); 
}

但一直以来他还只是 Firefox 中的一个实验属性,还没有看到其他浏览器有支持,但我期望在 2022 年的某一天能在主流浏览器上都能看到 CSS Grid 实现瀑布流的布局的效果。 ​

小结

上面列出的一些 CSS 新特性(也有新语法),具体有哪些新特性能在 2022 年中出现在浏览器中,还不得而知。上面的提到的一些特性其实已经在一个或多个浏览器中能体验了。感兴趣的可以自己尝试一下。另外要特别提出的是,上面列的 CSS 属性清单来自于社区有同行业专家以及我自己的一些积累和见解,如果有不对之处,欢迎一起探讨。如果所列清单有遗漏,也欢迎大家分享出来。 ​ 最后,希望上面的内容能帮助大家了解 CSS 的一些新东西,打开一些未知领域!谢谢!(^_^)