CSS
2022 年 CSS 新增了很多特性,例如容器查询、父选择器、子网格、级联层、新视口单位等,多项期待已久的功能已集成到常青浏览器(自动升级到最新版本的浏览器,包括 Chrome、Edge、Firefox 和 Safari)中。下面就来看看 2022 年 CSS 新增的 10 个实用功能吧!

1、颜色相关

下面来看看和 CSS 颜色相关的一些更新。CSS 工作组有两个新规范将改变在 Web 上使用颜色的方式:CSS Color Module Level 4(候选推荐)和 CSS Color Module Level 5(工作草案)。两者仍处于实验阶段,截至 2022 年 12 月,只有 Safari 已实现。

(1)新颜色函数语法

CSS Color Module Level 4 引入了颜色函数的新语法,例如rgb()hsl()。新语法省略了逗号,依靠空格来分隔颜色空间的每个通道。它还支持可选的 alpha 参数,从而不再需要额外的颜色函数,例如rgba()hsla()。逗号分隔的形式现在被规范称为“遗留语法”。

  1. /* 遗留语法 */
  2. background-color: hsl(270, 50%, 40%);
  3. color: hsla(0, 0%, 100%, 50%);
  4. /* 新语法 */
  5. background-color: hsl(270 50% 40%);
  6. color: hsl(0 0% 100% / 50%);

(2)新色彩空间

新的颜色规范为网络添加了大量新的颜色空间:

  • HWB:色调、白度、黑度
  • LCH:亮度、色度、色调
  • CIE L a b*
  • Oklab
  • Oklch
  • Display P3

这只是新增的颜色空间的一部分,其中一些色彩空间,比如 Display P3,提供了比 sRGB 空间更宽的色域,这意味着将可以使用更多颜色,并且这些颜色将比一直使用的颜色更鲜艳。

(3)相对颜色语法

CSS Color Module Level 5 通过引入相对颜色语法进一步增强了颜色函数。。此语法可以基于另一种颜色定义新颜色。可以通过首先使用 from 关键字定义原色,然后像往常一样在颜色函数中指定新颜色的通道来使用它。
当提供原始颜色时,可以访问“通道关键字”,允许引用颜色空间中的每个通道。关键字根据使用的颜色函数而变化。对于 rgb(),有 r、g 和 b 通道关键字;对于 oklch(),将拥有 l、c 和 h 关键字。对于每个颜色函数,还有一个 alpha 通道关键字,它是原始颜色的 alpha 通道。
可以在 calc() 表达式中使用这些通道关键字来修改原始颜色:

  1. /* 去色处理 */
  2. rgb(from tomato calc(r - 20) calc(g - 20) calc(b - 20));
  3. /* 半透明处理 */
  4. rgb(from tomato r g b / 50%)
  5. /* 在 oklch 中使颜色变暗 */
  6. oklch(from tomato calc(l - 0.1) c h);

除此之外,还可以跨颜色空间定义相对颜色。当使用一个空间中最初定义的颜色并使用不同的颜色空间定义新颜色时,浏览器将首先将原始颜色转换为新的颜色空间。
下面使用 Oklch 通过将色调旋转 120°(⅓ 圈)来定义基于 sRGB 中定义的原色的二次色:

  1. --primary: #005f73;
  2. --secondary: oklch(from var(--primary) l c calc(h + 120));

(4)color-mix()

CSS Color Module Level 5 规范还引入了一个 color-mix() 函数,允许在不同的颜色空间中混合颜色。

  1. color-mix(in lch, purple 50%, plum 50%)

上面代码中,会产生 50-50 的紫色和紫红色混合。

2、全新动态视口单位

新增的 CSS 视口单元用于处理带有动态工具栏的移动视口。
要想调整与视口一样大的尺寸,可以使用现有的视口单位 vw 和 vh:

  • vw:视口大小宽度的 1%;
  • vh:视口大小高度的 1%;

下面元素的宽度为 100vw,高度为 100vh,它将完全覆盖整个视口:
2022 年 CSS 新增的特性 - 图1
除了 vh 和 vw,还有:

  • vmin:vw或vh中的较小者。
  • vmax:vw或vh中的较大者。

这些单位在浏览器中都得到了很好的支持。虽然这些单位在桌面浏览器上运行良好,但在移动浏览器上就会存在一些问题,视口大小受动态工具栏存在与否的影响。这些是用户界面,例如地址栏和标签栏等。尽管视口大小可以更改,但 vw 和 vh 大小不会。因此,尺寸为 100vh 高的元素会从视口中溢出。
2022 年 CSS 新增的特性 - 图2
当向下滚动时,这些动态工具栏将缩回。在这种状态下,尺寸为 100vh 高的元素将覆盖整个视口。
2022 年 CSS 新增的特性 - 图3
为了解决这个问题,CSS 工作组规定了视口的各种状态:

  • 大视口:视口大小假设任何动态扩展和缩回的 UA 接口被缩回。
  • 小视口:视口大小假设任何动态扩展和缩回的 UA 接口都可以扩展。

2022 年 CSS 新增的特性 - 图4
新视口也有指定的单位:

  • 大视口的单位带有 lv 前缀,单位为 lvw、lvh、lvi、lvb、lvmin 和 lvmax。
  • 小视口的单位带有 sv 前缀,单位是 svw、svh、svi、svb、svmin 和 svmax。

除非调整视口本身的大小,否则这些视口百分比单位的大小是固定的。
2022 年 CSS 新增的特性 - 图5
除了大视口和小视口之外,还有一个动态视口,动态考虑了浏览器 UI:

  • 当动态工具栏展开时,动态视口等于小视口的大小。
  • 当动态工具栏缩回时,动态视口等于大视口的大小。

它的单位带有 dv 前缀:dvw、dvh、dvi、dvb、dvmin 和 dvmax。它们的尺寸在对应的 lv 和 sv 之间。
2022 年 CSS 新增的特性 - 图6
浏览器对这些单位的支持情况如下:
2022 年 CSS 新增的特性 - 图7
参考:https://web.dev/viewport-units/

3、@container:容器查询

CSS 容器查询是一种超越与视口相关的媒体查询的方法,而可以根据元素所在的容器修改元素的行为,不仅是依赖视口大小来更改元素的样式。
想要使用容器查询,首先需要在容器上定义 container-type

  1. main {
  2. container-type: inline-size;
  3. }

还可以使用 container-name 来命名容器,如果有多层容器,这将很有用,这样就可以更明确地了解哪些查询会影响元素。type 和 name 都可以使用简写的 container 属性来定义,其中 name 在前,并通过正斜杠与 type 分隔。

  1. .main {
  2. container: main / inline-size;
  3. }

然后就可以使用@container开始查询了。一旦满足该条件,CSS 将应用于该容器内的元素。
最后来看一个实际的应用:

  1. <main class="container">
  2. <article>...</article>
  3. <article>...</article>
  4. <article>...</article>
  5. </main>

现在就可以设置一个容器查询来更改文章样式及其任何后代的样式,这将基于 main 的宽度,因为它是容器元素。

  1. article {
  2. padding: 1rem;
  3. font-size: 1rem;
  4. }
  5. @container (min-width: 60ch) {
  6. article {
  7. padding: 2rem;
  8. font-size: 1.25rem;
  9. }
  10. }

这样,当文章的宽度小于 60ch 时,就会采用更小的 padding 和 font-size 值。
浏览器对容器查询的支持情况如下:
2022 年 CSS 新增的特性 - 图8

4、@layer:级联层

在 2022 年 2 月/3 月,所有现代浏览器都发布了 Cascade Layers(级联层),可以用来控制选择器如何交互,而不管它们的特殊性或代码顺序。下面来看一个例子:

  1. @layer default, theme, state;
  2. @layer default {
  3. button {
  4. background: rebeccapurple;
  5. color: white;
  6. }
  7. }
  8. @layer state {
  9. :disabled {
  10. background: dimgray;
  11. }
  12. }
  13. @layer theme {
  14. button.danger {
  15. background: maroon;
  16. }
  17. button.info {
  18. background: darkslateblue;
  19. }
  20. #call-to-action {
  21. background: mediumvioletred;
  22. }
  23. }

@layer 声明了一个级联层,同一层内的规则将级联在一起,这给予了开发者对层叠机制的更多控制。
上面例子中定义了多个级联层,当一个声明中具有多个级联层时,后定义的级联层具有更高的优先级。因此上面例子中,state 层具有更高的优先级,即使 theme 样式中具有更高的特定性(权重)并且在代码中出现得更晚。
还可以嵌套图层:

  1. @layer reset, framework, components, utilities;
  2. @layer components {
  3. @layer default, theme, state;
  4. @layer state {
  5. /* components.state 层 */
  6. :disabled { background: dimgray; }
  7. }
  8. }
  9. @layer components.state {
  10. /* components.state 层 */
  11. :focus-visible { outline: thin dashed hotpink; }
  12. }

层按照每个层名称首次出现在代码库中的顺序堆叠,后面的层名称优先于前面的层。这意味着可以允许它们隐式堆叠:

  1. @layer low { /* 最低层 */ }
  2. @layer medium { /* 中间层 */ }
  3. @layer high { /* 最高层 */ }

或者可以像上面例子一样,按顺序引入层名称来明确定义层顺序:

  1. @layer low, medium, high;

浏览器对级联层的支持情况如下:
2022 年 CSS 新增的特性 - 图9
可以看到,目前主流浏览器都支持 CSS 级联层功能。

5、:has:父选择器

:has()选择器可以检查父元素中是否存在特定的元素。例如,如果一个卡片组件中有图片,就给它添加一个display:flex。这以前在 CSS 中是无法实现的,但是新的 :has() 选择器就可以帮助我们选择包含特定元素的父元素。
在CSS中,无法根据元素中是否存在特定的元素来设置父元素的样式,要想实现这一点,就必须创建CSS类,并根据需要进行类的切换。来看下面的例子:
2022 年 CSS 新增的特性 - 图10
这里有两种卡片:包含图片和不包含图片。在CSS中需要这样做:

  1. /* 有图片的卡片 */
  2. .card {
  3. display: flex;
  4. align-items: center;
  5. gap: 1rem;
  6. }
  7. /* 没有图片的卡片 */
  8. .card-plain {
  9. display: block;
  10. border-top: 3px solid #7c93e9;
  11. }
  1. <!-- 有图片的卡片 -->
  2. <div class="card">
  3. <div class="card-image">
  4. <img src="awameh.jpg" alt="">
  5. </div>
  6. <div class="card-content">
  7. 卡片内容
  8. </div>
  9. </div>
  10. <!-- 没有图片的卡片 -->
  11. <div class="card card-plain">
  12. <div class="card-content">
  13. 卡片内容
  14. </div>
  15. </div>

这里创建了一个类card-plain,专门用于没有图片的卡片,在没有图片时就不需要flex布局。如果使用 CSS 中的父选择器 :has 就不需要再写这个类,只需要使用它来检查卡片中是否包含.card-image即可:

  1. .card:has(.card-image) {
  2. display: flex;
  3. align-items: center;
  4. }

根据 CSS 规范,**:has()** 选择器可以检查父元素是否包含至少一个元素,或者一个条件,例如输入是否获取到焦点。
:has() 选择器不仅可以检查父元素是否包含特定的子元素,还可以检查一个元素后面是否跟有另一个元素:

  1. .card h2:has(+ p) { }

这将检查 <h2> 元素是否直接跟在 <p> 元素之后。
也可以将它与表单元素一起使用来检查输入是否获取到了焦点:

  1. form:has(input:focused) {
  2. background-color: lightgrey;
  3. }

浏览器对 :has() 的支持情况如下:
2022 年 CSS 新增的特性 - 图11
可以看到,最新版的 Chrome、Edge、Safari 都已经支持了 :has() 选择器,而 Firfox 目前还不支持

6、:focus-visible

:focus-visible 是一个现代CSS 焦点选择器。今年 3 月,Safari 15.4 发布了 :focus-visible 伪类,不久之后,它成为常青浏览器中使用的默认元素焦点行为。
当元素匹配:focus伪类并且客户端的启发式引擎决定焦点应当可见 (在这种情况下很多浏览器默认显示“焦点框”。) 时,:focus-visible 伪类将生效。
注意:Firefox 需要通过较旧的前缀伪类 :-moz-focusring 来支持类似的功能。
下面来看一个例子,:focus-visible 选择器利用客户端的行为决定是否匹配。当使用鼠标点击控件和用键盘 tab 切换控件时会有所不同。

  1. <input value="默认样式s"><br>
  2. <button>默认样式</button><br>
  3. <input class="focus-only" value=":focus"><br>
  4. <button class="focus-only">:focus</button><br>
  5. <input class="focus-visible-only" value=":focus-visible"><br>
  6. <button class="focus-visible-only">:focus-visible</button>
  1. .focus-only:focus {
  2. outline: 2px solid black;
  3. }
  4. .focus-visible-only:focus-visible {
  5. outline: 4px dashed darkorange;
  6. }

效果如下:
2022 年 CSS 新增的特性 - 图12
这段代码的表现如下:

  • 默认样式:使用键盘控制时,input和button的边框都是细蓝色的;使用鼠标控制时,input的边框是细蓝色的,button的边框是细黑色的;
  • :focus:无论使用鼠标控制还是键盘控制,input和button的边框都是粗黑色的;
  • :focus-visible:使用键盘控制时,input和button的边框都是粗橙色的;使用鼠标控制时,input的边框是粗橙色的,button的边框是细黑色的;

使用这个伪类,就可以有效地根据用户的输入方式 (鼠标 or 键盘) 来展示不同形式的焦点。
浏览器对 :focus-visible 的支持情况如下:
2022 年 CSS 新增的特性 - 图13
可以看到,目前主流浏览器都已经支持 :focus-visible

7、color-scheme

color-scheme 是一个 CSS 属性,当用户选择系统配色方案时(系统配色方案的常见选择是“深色模式”和“浅色模式”),操作系统会对用户界面进行调整。这包括表单控件、滚动条和 CSS 系统颜色的使用值。

  1. :root {
  2. color-scheme: light dark;
  3. }

当把上面的代码复制到样式表中,页面就会根据操作系统的设置的暗/亮模式来显示不同的样式:
2022 年 CSS 新增的特性 - 图14
可以看到,当切换系统配色模式时,文字颜色,背景颜色,以及表单(滚动条、选择下拉框、单选框、复选框、输入框等)样式都发生了变化,这样就省去了很多两种模式下的样式定义工作。
通常,属性的值是以下几种:

  1. color-scheme: normal;
  2. color-scheme: light;
  3. color-scheme: dark;
  4. color-scheme: light dark;

其中:

  • normal:表示元素未指定任何配色方案,因此应使用浏览器的默认配色方案呈现。
  • light:表示可以使用操作系统亮色配色方案渲染元素。
  • dark:表示可以使用操作系统深色配色方案渲染元素。

需要注意,当light和right都有时,需要light在前,right在后。要想将整个页面配置为用户的配色方案首选项,就可以像上面一样,在 :root 元素上指定 color-scheme。
甚至可以仅在 HTML 中就可以获得深色模式:

  1. <head>
  2. <meta name="color-scheme" content="light dark" />
  3. </head>

浏览器对 color-scheme 的支持情况如下:
2022 年 CSS 新增的特性 - 图15

8、accent-color

accent-color 属性可以在不改变浏览器默认表单组件基本样式的前提下重置组件的颜色。该属性目前支持以下 HTML 控件元素:

  • 复选框:<input type="checkbox">
  • 单选框:<input type="radio">
  • 范围选择框:<input type="range">
  • 进度条:<progress>

下面来看一个例子:

  1. <form>
  2. <label>
  3. <input type="radio" name="radios" checked>
  4. 单选选中
  5. </label>
  6. <label>
  7. <input type="radio" name="radios">
  8. 单选未选中
  9. </label>
  10. <label>
  11. <input type="checkbox" checked>
  12. 多选选中
  13. </label>
  14. <label>
  15. <input type="checkbox">
  16. 多选未选中
  17. </label>
  18. <label>
  19. <input type="range">
  20. 范围
  21. </label>
  22. <label>
  23. <progress max="100" value="80">80%</progress>
  24. 进度条
  25. </label>
  26. </form>
  1. :root {
  2. accent-color: firebrick;
  3. }
  4. [type="radio"],
  5. [type="checkbox"] {
  6. font-size: inherit;
  7. width: 0.75em;
  8. height: 0.75em;
  9. }
  10. progress,
  11. [type="range"] {
  12. font-size: inherit;
  13. width: 10ch;
  14. }

效果如下:
2022 年 CSS 新增的特性 - 图16
可以看到,这些表单元素都变成定义的颜色。
需要注意,如果给表单元素设置了自定义样式,那 accent-color 就可能会失效。例如,将进度条的 border 设置为蓝色:

  1. progress {
  2. border: blue;
  3. }

样式会是下面这样,其并不符合预期(进度条边框为蓝色),出现了意料之外的效果。所以,如果使用了 accent-color 定义表单样式,就要尽量避免再给表单元素自定义其他样式:
2022 年 CSS 新增的特性 - 图17
浏览器对 accent-color 的支持情况如下:
2022 年 CSS 新增的特性 - 图18

9、scale、rotate、translate

2022 年 8 月,Chromium 完成了使用单个 rotate, scale, translate 属性来对 CSS 变换进行更细粒度的控制。
要想在 CSS 中使用变换,需要使用 transform 属性,该属性接受一个或多个 <transform-function>

  1. .target {
  2. transform: translateX(50%) rotate(30deg) scale(1.2);
  3. }

在上面的代码中,目标元素会在 X 轴上平移 50%,旋转 30 度,最后放大到 120%。虽然这样 transform 属性可以正常工作,但当想要单独更改这些值中的任何一个时,就会比较麻烦。
比如,想要在鼠标悬浮时更改 scale 的大小,就需要将 transform 属性中的所有函数都复制一遍,即使它们的值保持不变。

  1. .target:hover {
  2. transform: translateX(50%) rotate(30deg) scale(2);
  3. }

而在 Chrome 104 中,就可以使用rotate, scale, translate 属性来单独定义变换的这些部分。使用这些属性来重写前面的变换示例:

  1. .target {
  2. translate: 50% 0;
  3. rotate: 30deg;
  4. scale: 1.2;
  5. }

这样,如果想在某些情况下单独修改每个属性时,就不需要再复制其他没有变化的属性。
原始的 CSS 变换属性和新属性之间的一个主要区别是应用声明的变换顺序:

  • 使用 transform 时,变换函数会按照它们编写的顺序,从左到右;
  • 使用单独的变换属性时,顺序不是声明的顺序。而始终是:首先平移,然后旋转,最后缩放。

这意味着以下两端代码的执行结果是一样的:

  1. .transform-individual {
  2. translate: 50% 0;
  3. rotate: 30deg;
  4. scale: 1.2;
  5. }
  6. .transform-individual-alt {
  7. rotate: 30deg;
  8. translate: 50% 0;
  9. scale: 1.2;
  10. }

在这两种情况下,目标元素都会首先在 X 轴上平移 50%,然后旋转 30 度,最后缩放 1.2。
如果其中一个单独的变换属性与 transform 属性一起声明,则首先应用单独的变换(rotate, scale, translate),最后应用 transform。
浏览器对 rotate, scale, translate 的支持情况如下:
2022 年 CSS 新增的特性 - 图19

10、subgrid:子网格

subgrid 允许元素在行轴或列轴上继承其父元素的网格,主要解决当网格嵌套网格时,子网格的位置和轨道不能和父网格对齐的问题。使用子网格时,需要让 grid-template-columns 和 grid-template-rows 属性的值使用 subgrid 关键字。

  • grid-template-rows:基于网格行维度,定义网格线的名称和网格轨道的尺寸大小。
  • grid-template-columns:基于网格列维度,定义网格线的名称和网格轨道的尺寸大小。

下面来看一个例子,有一个嵌套网格,它正在为行和列创建自己的轨道。这些轨道是独立的,因此不会与父网格上的轨道对齐。

  1. <div class="grid">
  2. <div class="child"></div>
  3. <div class="child"></div>
  4. <div class="child"></div>
  5. <div class="child"></div>
  6. <div class="child"></div>
  7. <div class="subgrid">
  8. <div class="child"></div>
  9. <div class="child"></div>
  10. </div>
  11. </div>
  1. .subgrid {
  2. grid-column: auto / span 3;
  3. display: grid;
  4. gap: 10px;
  5. grid-template-columns: 1fr 1fr;
  6. }
  7. .grid {
  8. display: grid;
  9. grid-template-columns: 1fr 1fr 1fr 1fr;
  10. grid-auto-rows: minmax(100px, auto);
  11. max-width: 800px;
  12. margin: 2em auto;
  13. gap: 10px;
  14. padding: 10px;
  15. background-color: rgba(255,255,255,.8);
  16. }
  17. .grid > div {
  18. background-color: #2B3745;
  19. padding: 10px;
  20. }
  21. .subgrid > div {
  22. background-color: #D9D9E5;
  23. }

上面代码的效果如下:
2022 年 CSS 新增的特性 - 图20
下面将 grid-template-columns 的轨道列表替换为 subgrid 关键字。嵌套网格的列轨道现在与父级上的列轨道对齐。

  1. .subgrid {
  2. grid-template-columns: subgrid;
  3. }

更改完之后的效果如下:
2022 年 CSS 新增的特性 - 图21
只要父级本身就是一个网格,就可以继续将子网格继承到子级中。以下示例显示了三个网格,每个网格都嵌套在另一个网格中,并继承了其父级的轨道。子项指示哪个网格是它们的父项。

  1. <div class="one">
  2. <div class="child">Parent one</div>
  3. <div class="two">
  4. <div class="child">Parent two</div>
  5. <div class="three">
  6. <div class="child">Parent three</div>
  7. </div>
  8. </div>
  9. </div>
  1. .one {
  2. display: grid;
  3. grid-template-columns: repeat(8,1fr);
  4. max-width: 800px;
  5. margin: 2em auto;
  6. gap: 10px;
  7. padding: 10px;
  8. background-color: rgba(255,255,255,.8);
  9. }
  10. .two {
  11. grid-column: 2 / 8;
  12. grid-row: 2 ;
  13. display: grid;
  14. grid-template-columns: subgrid;
  15. border: 2px solid #96060A;
  16. }
  17. .three {
  18. display: grid;
  19. grid-column: 2 / 6;
  20. grid-row: 2;
  21. grid-template-columns: subgrid;
  22. border: 2px solid #1C2E01;
  23. }
  24. .child {
  25. background-color: #2B3745;
  26. padding: 10px;
  27. color: #fff;
  28. }

使用 Firefox DevTools 突出显示每个网格,突出显示外部网格显示子网格和网格项如何与该网格对齐:
2022 年 CSS 新增的特性 - 图22
浏览器对子网格的支持情况如下:
2022 年 CSS 新增的特性 - 图23