2022 年 CSS 现状

2022 年 Google IO 大会上介绍了如今和未来的 Web 样式设置功能,以及一些额外功能。

2022 年预计将是 CSS 功能发布和协作式浏览器功能发布方面发展史上最辉煌的一年,我们携手实现 14 项功能!

概览

本博文是 2022 年 Google IO 大会上演讲的文章形式。它不是关于每个功能的深入指南,而是介绍和简要概述来激发您的兴趣,提供广度而不是深度。如果您有兴趣,请查看某一部分的末尾,获取更多信息的资源链接。

目录

请使用以下列表跳转到感兴趣的主题:

浏览器兼容性

许多 CSS 功能被设置为合作发布的主要原因是 Interop 2022 的工作成果。在研究 Interop 工作之前,请务必了解 Compat 2021 的工作。

Compat 2021

在开发者通过问卷调查获得反馈的推动下,我们在 2021 年的目标是稳定当前功能、改进测试套件,并提高浏览器的以下五项功能的合格分数:

  1. sticky定位
  2. 大小为 aspect-ratio
  3. flex”布局
  4. grid”布局
  5. transform 定位和动画

测试分数全面提高,这证明了更高的稳定性和可靠性。热烈祝贺我们的各个团队!

Interop 2022

今年,各浏览器相聚在一起,讨论他们打算在哪些功能和优先事项方面展开合作,让他们齐心协力。他们计划为开发者提供以下 Web 功能:

  1. @layer
  2. 颜色空间和颜色函数
  3. contain 属性
  4. <dialog>
  5. 表单兼容性
  6. 滚动
  7. 子网格
  8. 排版
  9. 视口单位
  10. Web 兼容性

这是一个激动人心、雄心勃勃的清单,我迫不及待想看到它。

2022 年度新作

不出所料,CSS 2022 的现状受到 Interop 2022 工作的严重影响。

级联层

浏览器支持

  • 99
  • 99
  • 97
  • 15.4

来源

@layer 之前,发现的已加载样式表的顺序非常重要,因为最后加载的样式可能会覆盖之前加载的样式。这就导致了一种精细的管理条目样式表,开发者需要先加载不太重要的样式,稍后再加载更重要的样式。我们提供各种方法来协助开发者管理这种重要性,例如 ITCSS

借助 @layer,条目文件可以提前预定义图层及其顺序。然后,在加载或定义样式时,您可以将这些样式放置在图层中,这样可以保留样式替换的重要性,但不能进行精细管理的加载编排。

该视频展示了定义的级联层如何实现更自由、更自由的制作和加载过程,同时仍根据需要维护级联。

Chrome 开发者工具有助于直观呈现来自哪些图层的样式:

Chrome Devtools 的“样式”边栏的屏幕截图,突出显示了样式在新图层组中的显示方式。

资源

子网格

浏览器支持

  • 117
  • 117
  • 71
  • 16

来源

subgrid 之前,另一个网格内部的网格无法与其父级单元格或网格线对齐。每个网格布局都是独一无二的。许多设计师会在整个设计上放置一个网格,并不断对齐其中的项目,而这在 CSS 中是无法做到的。

subgrid 之后,网格的子级可以采用其父级的列或行作为自己的列,并将自身或子级与其对齐!

在下面的演示中,body 元素创建了一个包含三列的经典网格:中间列名为 main,左右列将其行命名为 fullbleed。然后,正文中的每个元素(<nav><main>)都会通过设置 grid-template-columns: subgrid 来采用正文中的已命名行。

​​body {
  display: grid;
  grid-template-columns:
    [fullbleed-start]
    auto [main-start] min(90%, 60ch) [main-end] auto
    [fullbleed-end]
  ;
}

body > * {
  display: grid;
  grid-template-columns: subgrid;
}

最后,<nav><main> 的子项可以使用 fullbleedmain 列及线条自行对齐或调整大小。

.main-content {
  grid-column: main;
}

.fullbleed {
  grid-column: fullbleed;
}

开发者工具可以帮助您查看线和子网格(目前仅限 Firefox 浏览器)。在下图中,父网格和子网格已叠加。现在,设计人员与设计人员对布局的想法一致。

subgrid 演示的屏幕截图,其中使用 Chrome Devtools 网格叠加工具显示由 CSS 定义的线条。

在开发者工具的“元素”面板中,您可以看到哪些元素是网格和子网格,这对于调试或验证布局非常有用。

Chrome Devtools 的“Elements”面板的屏幕截图,其中标记了哪些元素采用网格布局或子网格布局。
Firefox 开发者工具的屏幕截图

资源

容器查询

浏览器支持

  • 105
  • 105
  • 110
  • 16

来源

@container 之前,网页的元素只能响应整个视口的大小。这非常适合宏布局,但对于微布局(外部容器不是整个视口)来说,布局无法进行相应调整。

@container 之后,元素可以响应父级容器的大小或样式!唯一需要注意的是,容器必须将自身声明为可能的查询目标,这是一项小要求,但意义重大。

/* establish a container */
.day {
  container-type: inline-size;
  container-name: calendar-day;
}

这些样式使得事件元素能够查询以下视频中的“周一”“周二”“周三”“周四”和“周五”列。

演示Una Kravets 提供

以下 CSS 用于查询 calendar-day 容器的大小,然后调整布局和字体大小:

@container calendar-day (max-width: 200px) {
  .date {
    display: block;
  }

  .date-num {
    font-size: 2.5rem;
    display: block;
  }
}

下面是另一个示例:一个图书组件会自行调整以适应被拖动到的列内的可用空间:

演示 作者:Max Böck

Una 正确评估情况是新回复。使用 @container 时,需要做出许多激动人心且有意义的设计决策。

资源

accent-color

浏览器支持

  • 93
  • 93
  • 92
  • 15.4

来源

accent-color 之前,当您想要一个具有品牌匹配颜色的表单时,最终可能会用到复杂的库或 CSS 解决方案,随着时间的推移,这些库或 CSS 解决方案变得越来越难以管理。虽然他们为您提供了所有选项,并且有望包含无障碍功能,但继续选择是使用内置组件还是采用自己的组件就变得很繁琐。

accent-color 之后,一行 CSS 会为内置组件带来品牌颜色。除了色调调节之外,浏览器还会为组件的辅助部分智能地选择对比鲜明的颜色,并适应系统配色方案(浅色或深色)。

/* tint everything */
:root {
  accent-color: hotpink;
}

/* tint one element */
progress {
  accent-color: indigo;
}

并排比较浅色和深色 HTML 元素。

如需详细了解 accent-color,请查看我在 web.dev 上发布的博文,其中详细介绍了这个实用 CSS 属性的许多其他方面。

资源

颜色级别 4 和 5

在过去几十年里,sRGB 一直是网络的主要存在,但在不断扩展的数字世界中,高清显示屏和预先配备了 OLED 或 QLED 屏幕的移动设备的 sRGB 已不够强大。此外,动态页面会迎合用户偏好,颜色管理一直是设计师、设计系统和代码维护人员日益关注的方面。

不过,在 2022 年还不会 - CSS 有一些新的颜色函数和空间: - 能够达到显示屏的高清颜色功能的颜色。 - 与 intent 匹配的颜色空间,例如感知一致性。 - 显著改变插值结果的渐变的颜色空间。 - 颜色函数,可帮助您混合和对比,以及选择在哪个空间执行工作。

在所有颜色特征之前,设计系统需要预先计算适当的对比色,并确保调色板具有适当的鲜艳度,而预处理器或 JavaScript 则负责完成繁杂的工作。

完成所有颜色特征后,浏览器和 CSS 便可以动态地及时完成所有工作。CSS 可以编排和计算,而无需向用户发送大量 KB 的 CSS 和 JavaScript 以启用主题和数据可视化颜色。CSS 也更适合在使用之前检查是否支持,或者妥善处理回退。

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

@supports (color: lab(0% 0 0)) {
  .neon-pink {
    --neon-glow: lab(150% 160 0);
  }
}

hwb()

浏览器支持

  • 101
  • 101
  • 96
  • 15

来源

HWB 表示色调、白度和黑度。它表现为一种易于人类理解的颜色,因为它只是一种色调,以及一定程度的白色或黑色,用于提亮或变暗。混入白色或黑色的音乐人可能会发现自己很喜欢这种添加颜色语法的技巧。

使用此颜色函数会从 sRGB 颜色空间生成颜色,与 HSL 和 RGB 相同。就 2022 年的新事物而言,这虽然不会带给您新的色彩,但可能会让语法和思维模式爱好者更轻松地处理一些任务。

资源

色彩空间

颜色的表示方式是通过颜色空间实现的。每种颜色空间在颜色处理方面提供了各种功能和权衡。有些可能会将所有亮色打包在一起;有些可能会根据亮度先将它们对齐。

2022 年,CSS 将提供 10 种新的颜色空间,每个颜色空间都具有独特的功能,可帮助设计师和开发者显示、选择和混合颜色。以前,sRGB 是处理颜色的唯一选项,但现在 CSS 释放了新的潜力和新的默认颜色空间 LCH。

color-mix()

浏览器支持

  • 111
  • 111
  • 113
  • 16.2

来源

color-mix() 之前,开发者和设计人员需要使用 Sass 等预处理器在浏览器看到颜色之前混合颜色。大多数颜色混合函数也没有提供用于指定混入哪个颜色空间的选项,这有时会导致令人困惑的结果。

color-mix() 之后,开发者和设计人员可以在浏览器中混合颜色以及所有其他样式,而无需运行构建流程或包含 JavaScript。此外,他们还可以指定要混合到哪个颜色空间,或者使用 LCH 的默认混合颜色空间。

通常,某个品牌颜色被用作基础颜色,系统会根据该颜色创建变体,例如,对于悬停样式,颜色较浅或较深。使用 color-mix() 时,如下所示:

.color-mix-example {
  --brand: #0af;

  --darker: color-mix(var(--brand) 25%, black);
  --lighter: color-mix(var(--brand) 25%, white);
}

如果您想在 srgb 等其他颜色空间中混合这些颜色,请更改颜色:

.color-mix-example {
  --brand: #0af;

  --darker: color-mix(in srgb, var(--brand) 25%, black);
  --lighter: color-mix(in srgb, var(--brand) 25%, white);
}

以下是使用 color-mix() 的主题设置演示。请尝试更改品牌颜色,并观察主题更新:

2022 年,您可以尽情在样式表中混合使用不同颜色空间的颜色!

资源

color-contrast()

浏览器支持

  • x
  • x
  • x

来源

color-contrast() 之前,样式表作者需要提前了解可访问的颜色。通常,调色板会在色样上显示黑色或白色文本,以向颜色系统的用户表明需要哪种文本颜色才能与该色样形成适当的对比。

3 个 Material 调色板的屏幕截图,其中显示了 14 种颜色及其对应的白色或黑色对比度文字。
2014 年 Material Design 调色板示例

color-contrast() 之后,样式表作者可以将任务完全分流到浏览器。您不仅可以让浏览器自动选择黑色或白色,还可以为设计系统提供一系列合适的颜色,并让它选择第一种颜色来达到您期望的对比度。

下面是 HWB 调色板集演示的屏幕截图,其中浏览器根据色样颜色自动选择文本颜色:

HWB 演示的屏幕截图,其中每个调色板都有不同的浅色或深色文本配对(由浏览器确定)。
试用演示

该语法的基本内容如下所示,其中灰色传递给函数,浏览器确定黑色还是白色对比度最高:

color: color-contrast(gray);

您还可以使用一个颜色列表对该函数进行自定义,从所选颜色中挑选出对比度最高的颜色:

color: color-contrast(gray vs indigo, rebeccapurple, hotpink);

最后,如果最好不要从列表中选择对比度最高的颜色,则可以提供目标对比度,并选择第一种要通过它的颜色:

color: color-contrast(
  var(--bg-blue-1)
  vs
  var(--text-lightest), var(--text-light), var(--text-subdued)
  to AA /* 4.5 could also be passed */
);

此函数可用于不仅仅可用于文本颜色,不过我估计这将是其主要用例。想一想,如果将选择的对比鲜明的颜色内置到 CSS 语言本身中,实现可访问且清晰易读的界面有多容易。

资源

相对颜色语法

浏览器支持

  • 111
  • 111
  • 113
  • 15

来源

在使用相对颜色语法之前,为了计算颜色并进行调整,需要将颜色通道单独放入自定义属性中。这一限制还使 HSL 成为操控颜色的主要颜色函数,因为可以使用 calc() 直接调整色调、饱和度或亮度。

遵循相对颜色语法后,任何空间中的任何颜色都可以在一行 CSS 中解构、修改和返回为颜色。对 HSL 没有更多限制 - 可以在所需的任何颜色空间中进行操作,并且需要创建很多更少的自定义属性来辅助它。

以下语法示例中提供了一个基本十六进制值,并创建了与其相关的两种新颜色。第一种颜色 --absolute-change 会根据基础颜色在 LCH 中创建新颜色,然后继续使用 75% 替换基础颜色的亮度,从而保持色度 (c) 和色调 (h)。第二种颜色 --relative-change 会在 LCH 中基于基础颜色创建一种新颜色,但这次会将色度 (c) 减少 20%。

.relative-color-syntax {
  --color: #0af;
  --absolute-change: lch(from var(--color) 75% c h);
  --relative-change: lch(from var(--color) l calc(c-20%) h);
}

这类似于混合颜色,但更类似于改变,而不是混合。您可以从另一种颜色投射颜色,访问由所用颜色函数命名的三个通道值,并有机会调整这些通道。总的来说,这是一个非常酷又强大的颜色语法。

在下面的演示中,我使用了相对颜色语法创建了基础颜色的较浅和较深变体,并使用 color-contrast() 确保标签具有适当的对比度:

包含 3 列的屏幕截图,每列颜色比中心列更暗或更浅。
试用演示

此函数还可用于生成调色板。在下面的演示中,我们根据提供的基色生成了整个调色板。这组 CSS 支持所有各种调色板,每个调色板仅提供不同的基础。此外,由于我使用了 LCH,请再看看效果,即使是调色板,也不会看到热点或死点,这得益于这种颜色空间。

:root {
  --_color-base: #339af0;

  --color-0:  lch(from var(--_color-base) 98% 10 h);
  --color-1:  lch(from var(--_color-base) 93% 20 h);
  --color-2:  lch(from var(--_color-base) 85% 40 h);
  --color-3:  lch(from var(--_color-base) 75% 46 h);
  --color-4:  lch(from var(--_color-base) 66% 51 h);
  --color-5:  lch(from var(--_color-base) 61% 52 h);
  --color-6:  lch(from var(--_color-base) 55% 57 h);
  --color-7:  lch(from var(--_color-base) 49% 58 h);
  --color-8:  lch(from var(--_color-base) 43% 55 h);
  --color-9:  lch(from var(--_color-base) 39% 52 h);
  --color-10: lch(from var(--_color-base) 32% 48 h);
  --color-11: lch(from var(--_color-base) 25% 45 h);
  --color-12: lch(from var(--_color-base) 17% 40 h);
  --color-13: lch(from var(--_color-base) 10% 30 h);
  --color-14: lch(from var(--_color-base) 5% 20 h);
  --color-15: lch(from var(--_color-base) 1% 5 h);
}
15 个调色板都由 CSS 动态生成的屏幕截图。
试用演示

希望现在您已了解如何根据颜色空间和不同颜色函数各自的优缺点将其用于不同的目的。

资源

渐变颜色空间

在渐变颜色空间推出之前,sRGB 是所使用的默认颜色空间。sRGB 通常较为可靠,但也存在一些缺陷,例如灰色盲区

网格中的 4 种渐变效果,全部由青色到深粉色。LCH 和 LAB 的活力更一致,其中 sRGB 中间的饱和度偏低。

在渐变颜色空间之后,告知浏览器要用于颜色插值的颜色空间。这样,开发者和设计人员就可以选择他们偏好的渐变。默认颜色空间也会更改为 LCH,而不是 sRGB。

语法添加是在渐变方向之后添加的,它使用新的 in 语法,且可选:

background-image: linear-gradient(
  to right in hsl,
  black, white
);

background-image: linear-gradient(
  to right in lch,
  black, white
);

以下是从黑到白色的基本且基本渐变。请查看每个颜色空间中的结果范围。而有些图片呈现到深黑色的时间比其他部分早,有些则呈现得太晚才呈现为白色。

黑白对比,显示了 11 个颜色空间。

在下一个示例中,黑色过渡为蓝色,因为这是渐变的已知问题空间。在颜色插值期间,大多数颜色空间都呈现为紫色,正如我自己认为的那样,当颜色在颜色空间内从 A 点传输到 B 点时,会呈现出紫色。由于渐变将采用从 A 点到 B 点的直线,因此颜色空间的形状会大大改变路径沿途经过的站点。

以蓝色和黑色比较显示了 11 个颜色空间。

如需进行更深入的探索、示例和评论,请参阅此 Twitter 会话

资源

inert

浏览器支持

  • 102
  • 102
  • 112
  • 15.5

来源

inert 之前,最好将用户的注意力吸引到页面或应用中需要立即加以关注的区域。这种引导式焦点策略被称为焦点陷阱,因为开发者会将焦点置于交互式空间中,监听焦点更改事件,如果焦点离开交互式空间,便会被强制放回。系统会引导使用键盘或屏幕阅读器的用户返回到互动空间,以确保任务完成后再继续。

inert 之后,不需要进行 trap,因为您可以冻结或保护网页或应用的整个部分。当文档的这些部分处于闲置状态时,尝试点击和焦点更改只是不可用的。也可以将其视为守卫而不是陷阱,其中 inert 无意让您留在某个地方,而是让其他地方不可用。

JavaScript alert() 函数就是一个很好的例子:

网站显示为互动式,然后调用 alert(),网页不再处于活跃状态。

请注意,在上一个视频中,在调用 alert() 之前,可以通过鼠标和键盘访问网页。显示提醒对话框弹出式窗口后,页面的其余部分就会冻结,即 inert。用户的焦点位于提醒对话框中,而且无其他位置。当用户与提醒函数互动并完成提醒函数请求后,页面就会重新进入互动页面。inert 让开发者能够轻松实现同样的引导式焦点体验。

以下小型代码示例可展示其工作原理:

<body>
  <div class="modal">
    <h2>Modal Title</h2>
    <p>...<p>
    <button>Save</button>
    <button>Discard</button>
  </div>
  <main inert>
    <!-- cannot be keyboard focused or clicked -->
  </main>
</body>

对话框就是一个很好的例子,但 inert 对滑出侧边菜单用户体验等也很有用。当用户滑出侧边菜单时,不允许鼠标或键盘与其后面的页面交互;这对用户来说有点棘手。相反,当显示侧边菜单时,应让页面保持惯性,现在用户必须关闭侧边菜单或在侧边菜单内导航,再也不会发现自己在打开的菜单的其他位置离开了页面。

资源

COLRv1 字体

在 COLRv1 字体推出之前,Web 使用的是 OT-SVG 字体,这种字体也是一种开放格式,适用于具有渐变以及内置颜色和效果的字体。不过,它们可能会变得非常大,虽然它们允许修改文本,但自定义的范围不大。

在采用 COLRv1 字体之后,Web 字体的体积更小、可向量扩展、可调整位置、支持渐变且支持混合模式,而且这些字体可接受参数来按用例自定义字体或与品牌匹配。

比较可视化图表和条形图,显示 COLRv1 字体更清晰和更小。
图片来源于 https://developer.chrome.com/blog/colrv1-fonts/

以下是 Chrome 开发者博文中关于表情符号的示例。也许您已经注意到,如果放大表情符号的字体大小,它就会不清晰。它是图片而不是矢量图。在应用中,使用表情符号时,通常会将其替换为更优质的资源。使用 COLRv1 字体,表情符号是矢量且美观的:

图标字体可以使用这种格式实现一些令人惊叹的效果,例如提供自定义双色调调色板,等等。加载 COLRv1 字体与加载任何其他字体文件一样:

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

自定义 COLRv1 字体是使用 @font-palette-values 完成的,这是一种特殊的 CSS 规则,用于将一组自定义选项分组并命名到一个 bundle 中以供日后参考。请注意如何像自定义属性一样指定自定义名称(以 -- 开头):

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

@font-palette-values --colorized {
  font-family: "Bungee Spice";
  base-palette: 0;
  override-colors: 0 hotpink, 1 cyan, 2 white;
}

--colorized 作为自定义设置的别名后,最后一步是将调色板应用于使用相应颜色字体系列的元素:

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

@font-palette-values --colorized {
  font-family: "Bungee Spice";
  base-palette: 0;
  override-colors: 0 hotpink, 1 cyan, 2 white;
}

.spicy {
  font-family: "Bungee Spice";
  font-palette: --colorized;
}
带有“DUNE”字样的 Bungee Spice 字体的屏幕截图。
使用自定义颜色显示的 Bungee Spice 字体,来源于 https://developer.chrome.com/blog/colrv1-fonts/

随着越来越多的可变字体和彩色字体的推出,网页排版正朝着实现丰富自定义和创意表达的道路迈进。

资源

视口单位

显示设备屏幕、浏览器窗口和 iframe 如何具有不同视口的图片。

在新视口变体推出之前,网站提供了物理单元来协助适应视口。分别为高度、宽度、最小尺寸 (vmin) 和最大边 (vmax)。这些方法在很多方面都表现良好,但移动浏览器也带来了复杂性。

在移动设备上,加载页面时,系统会显示包含网址的状态栏,此栏会占用部分视口空间。在几秒内以及进行一些互动后,状态栏可能会滑开,为用户提供更大的视口体验。但是,当该横条滑出时,视口高度会发生变化,并且任何 vh 单元都会随着其目标尺寸的变化而移动和调整大小。后来几年,vh 单元就特别需要确定该使用两种视口尺寸中的哪一种,因为它会在移动设备上造成糟糕的视觉布局问题。已确定 vh 将始终表示最大视口。

.original-viewport-units {
  height: 100vh;
  width: 100vw;
  --size: 100vmin;
  --size: 100vmax;
}

在新视口变体推出后,“小型”“大型”和“动态”视口单位可供使用,并为物理视口添加了逻辑等效项。其理念是让开发者和设计人员能够选择用于给定场景的单元。或许当状态栏消失时,细微的布局偏移是没有问题的,这样便可放心使用 dvh(动态视口高度)。

一张包含三部手机的图,帮助说明 DVH、LVH 和 SVH。DVH 示例手机有两条竖线,一条位于搜索栏底部和视口底部,另一条介于搜索栏上方(系统状态栏下方)到视口底部;它显示了 DVH 如何采用这两种长度中的任意一种。LVH 显示在中间,在设备状态栏底部和手机视口的按钮之间有一条线。最后一个是 SVH 单位示例,显示了从浏览器搜索栏底部到视口底部的一条线

下面列出了新视口变体提供的所有新视口单元选项:

高度视口单位
​​.new-height-viewport-units {
  height: 100vh;
  height: 100dvh;
  height: 100svh;
  height: 100lvh;
  block-size: 100vb;
  block-size: 100dvb;
  block-size: 100svb;
  block-size: 100lvb;
}
宽度视口单位
.new-width-viewport-units {
  width: 100vw;
  width: 100dvw;
  width: 100svw;
  width: 100lvw;
  inline-size: 100vi;
  inline-size: 100dvi;
  inline-size: 100svi;
  inline-size: 100lvi;
}
最小视口边单位
.new-min-viewport-units {
  --size: 100vmin;
  --size: 100dvmin;
  --size: 100svmin;
  --size: 100lvmin;
}
最大视口侧边单位
.new-max-viewport-units {
  --size: 100vmax;
  --size: 100dvmax;
  --size: 100svmax;
  --size: 100lvmax;
}

希望这些设计能帮助开发者和设计人员灵活实现视口自适应设计。

资源

:has()

浏览器支持

  • 105
  • 105
  • 121
  • 15.4

来源

:has() 之前,选择器subject 始终位于最后。例如,此选择器的主题是一个列表项:ul > li。伪选择器可以更改选择器,但不会更改主题:ul > li:hoverul > li:not(.selected)

:has() 之后,在提供关于子元素的查询时,元素树中较高位置的主题可以保留为主题:ul:has(> li)。很容易理解 :has() 如何获得“父级选择器”的通用名称,因为在这种情况下,选择器的主题现在是父级。

下面是一个基本语法示例,其中 .parent 类仍是主题,但仅当子元素具有 .child 类时才会选择该类:

.parent:has(.child) {...}

在以下示例中,<section> 元素为正文,但仅当其中一个子级具有 :focus-visible 时,选择器才会匹配:

section:has(*:focus-visible) {...}

一旦更多实际用例显而易见,:has() 选择器就会开始成为一个非常棒的实用程序。例如,目前无法在封装图像时选择 <a> 标记,因此很难教授锚标记在该用例中如何更改其样式。可以通过 :has() 实现,不过:

a:has(> img) {...}

这些都是 :has() 看起来只像父级选择器的示例。请考虑 <figure> 元素内图片的用例,如果图片具有 <figcaption>,请调整图片的样式。在以下示例中,我们选择了带“说明”的图形,然后基于该背景添加图片。使用 :has() 不会更改主题,因为我们定位的主题是图片而不是数字:

figure:has(figcaption) img {...}

游戏组合似乎无穷无尽。可以将 :has()数量查询结合使用,并根据子项的数量调整 CSS 网格布局。将 :has()交互式伪类状态结合使用,即可创建能以全新创造方式响应的应用。

使用 @supports 及其 selector() 函数即可轻松检查支持情况,该函数可在使用前测试浏览器是否理解语法:

@supports (selector(:has(works))) {
  /* safe to use :has() */
}

资源

2022 年及未来

所有这些令人惊叹的功能于 2022 年推出后,仍有许多工作将难以完成。下一部分将介绍一些遗留问题,以及我们正在为解决这些问题而积极开发的解决方案。这些解决方案都处于实验阶段,即使可以在浏览器中指定或通过标志提供这些解决方案。

从后面几节中得出的结论应该令人欣慰的是,许多公司都在为所列出的问题寻求解决方案,而不是这些解决方案将在 2023 年发布。

类型松散的自定义属性

浏览器支持

  • 85
  • 85
  • 16.4

来源

CSS 自定义属性的效果非常出色。它们允许将各种内容存储在命名变量中,然后对命名变量进行扩展、计算、共享等。事实上,它们非常灵活,如果具有一些不太灵活,那就更好了。

假设 box-shadow 使用自定义属性作为其值:

box-shadow: var(--x) var(--y) var(--blur) var(--spread) var(--color);

这一切运行正常,直到其中任何一个属性变为 CSS 不接受的值(例如 --x: red)。如果有任何一个嵌套变量缺失或设置为无效的值类型,整个阴影会中断。

这正是 @property 的用武之地:--x 可以成为类型化自定义属性,不再宽松和灵活,但通过一些定义的边界是安全的:

@property --x {
  syntax: '<length>';
  initial-value: 0px;
  inherits: false;
}

现在,当 box-shadow 使用 var(--x) 并稍后尝试添加 --x: red 时,red 将被忽略,因为它不是 <length>。这意味着,即使向阴影中的某个自定义属性指定了无效值,阴影仍会继续工作。 它会还原为 0pxinitial-value,而不是失败。

动画

除了类型安全外,它还为动画打开了许多窗口。CSS 语法的灵活性使得为某些操作(例如渐变)制作动画效果是不可能的。@property 可以起到帮助作用,因为类型化 CSS 属性可以在原本过于复杂的插值内部告知浏览器开发者的意图。它本质上限制了可能性的范围,因为浏览器可以为某个样式的各个方面添加动画效果,而之前无法做到这一点。

请看下面的演示示例,其中径向渐变用于绘制叠加层的一部分,从而形成聚光灯聚焦效果。JavaScript 会在按下 alt/优化键时设置鼠标 x 和 y,然后将焦点大小更改为较小的值(例如 25%),从而在鼠标位置创建聚光灯焦点圆圈:

试用演示
.focus-effect {
  --focal-size: 100%;
  --mouse-x: center;
  --mouse-y: center;

  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    transparent 0%,
    transparent var(--focal-size),
    black 0%
  );
}

不过,无法为渐变添加动画效果。它们太灵活且过于复杂,以至于浏览器无法“只是派生”出您想要的动画效果。不过,使用 @property 时,可以单独为一个属性输入类型和动画,这样浏览器就能够轻松理解其 intent。

使用此焦点效果的视频游戏始终会以动画形式呈现圆圈,即从大圆圈到针孔圆圈。下面展示了如何在演示中使用 @property,以便浏览器为渐变遮罩添加动画效果:

@property --focal-size {
  syntax: '<length-percentage>';
  initial-value: 100%;
  inherits: false;
}

.focus-effect {
  --focal-size: 100%;
  --mouse-x: center;
  --mouse-y: center;

  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    transparent 0%,
    transparent var(--focal-size),
    black 0%
  );

  transition: --focal-size .3s ease;
}
试用演示

现在,浏览器能够为渐变大小添加动画效果,因为我们已将修改的表面积缩小为一个属性,并输入了相应值,以便浏览器可以智能地插值长度。

@property 的功能远不止这些,但这些微小的启用就很有意义。

资源

去了min-widthmax-width

在媒体查询范围之前,CSS 媒体查询使用 min-widthmax-width 来区分不同条件和不同条件。具体可能如下所示:

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

在媒体查询范围之后,同一媒体查询可能如下所示:

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

同时使用 min-widthmax-width 的 CSS 媒体查询可能如下所示:

@media (min-width: 320px) and (max-width: 1280px) {
  …
}

在媒体查询范围之后,同一媒体查询可能如下所示:

@media (320px <= width <= 1280px) {
  …
}

根据您的编码背景,其中一个代码看起来比另一个更容易清晰。得益于这些规范的新增,开发者能够选择他们偏好的实现方式,甚至可以互换使用。

资源

无媒体查询变量

@custom-media 之前,媒体查询必须不断重复,或者依靠预处理器在构建期间基于静态变量生成适当的输出。

@custom-media 之后,CSS 允许为媒体查询添加别名以及引用这些查询,就像自定义属性一样。

为内容命名非常重要:它可以让用途与语法保持一致,使内容更易于共享,也更易于在团队中使用。以下是几个不同项目之间的自定义媒体查询:

@custom-media --OSdark  (prefers-color-scheme: dark);
@custom-media --OSlight (prefers-color-scheme: light);

@custom-media --pointer (hover) and (pointer: coarse);
@custom-media --mouse   (hover) and (pointer: fine);

@custom-media --xxs-and-above (width >= 240px);
@custom-media --xxs-and-below (width <= 240px);

定义好这两个变量后,我可以按如下方式使用其中一个:

@media (--OSdark) {
  :root {
    …
  }
}

查看我在 CSS 自定义属性库 Open Props 中使用的自定义媒体查询的完整列表

资源

嵌套选择器非常实用

@nest 之前,样式表中有许多重复。当选择器很长并且每个选择器都针对细微差异时,这会特别麻烦。嵌套的便利性是采用预处理器的最常见原因之一。

@nest 之后,重复内容就消失了。启用预处理器的嵌套的几乎所有功能都将内置到 CSS 中。

article {
  color: darkgray;
}

article > a {
  color: var(--link-color);
}

/* with @nest becomes */

article {
  color: darkgray;

  & > a {
    color: var(--link-color);
  }
}

对我来说,嵌套最重要的是,除了不在嵌套选择器中重复 article 之外,样式上下文仍保留在一个样式块中。读者无需从一个选择器及其样式跳转到另一个具有样式的选择器(示例 1),而是可以一直停留在报道的上下文中,并看到该报道拥有该报道中的链接。关系和样式 intent 捆绑在一起,因此 article 看起来就像拥有自己的样式。

所有权也可以视为集中化。无需在样式表中四处查找相关样式,它们都可以在一个上下文中嵌套在一起找到。这适用于父子关系,也适用于子对父关系。

假设有一个组件子项,它希望在不同的父上下文中自行调整,而不是父项拥有样式并更改子项:

/* parent owns this, adjusting children */
section:focus-within > article {
  border: 1px solid hotpink;
}

/* with @nest becomes */

/* article owns this, adjusting itself when inside a section:focus-within */
article {
  @nest section:focus-within > & {
     border: 1px solid hotpink;
  }
}

@nest 整体而言有助于实现更健康的组织方式、集中化和所有权。组件可以对自己的样式进行分组并拥有自己的样式,而不是将它们分散在其他样式块中。这些例子看起来可能很小,但为了方便和易读,它可能会产生非常大的影响。

资源

限定作用域样式非常困难

浏览器支持

  • 118
  • 118
  • x
  • 17.4

@scope 之前,存在许多策略是因为 CSS 中的样式会进行级联、继承,并且默认情况下处于全局范围。CSS 的这些功能对于很多事情来说非常方便,但对于复杂的网站和应用(具有可能有许多不同组件样式),级联的全局空间和性质可能会使样式看起来像在泄漏。

@scope 之后,不仅可以将样式的范围限定为仅在上下文(例如类)内,还可以清晰地指明样式的结束位置和不会继续级联或继承的位置。

在以下示例中,BEM 命名惯例范围可以逆转为实际 intent。BEM 选择器会尝试根据命名惯例将 header 元素的颜色范围限定为 .card 容器。这要求标头上具有该类名称,从而完成目标。如果使用 @scope,则无需命名惯例,即可在不标记标题元素的情况下完成相同的目标:

.card__header {
  color: var(--text);
}

/* with @scope becomes */

@scope (.card) {
  header {
    color: var(--text);
  }
}

下面是另一个示例,这个示例不太具体,只不过是关于 CSS 的全局范围性质的。深色主题和浅色主题必须在样式表中共存,其中顺序决定着效果最佳的样式。通常,这意味着深色主题样式在浅色主题之后;这会将浅色样式设置为默认样式,并将深色样式设置为可选样式。使用 @scope 避免与排序和作用域冲突:

​​@scope (.light-theme) {
  a { color: purple; }
}

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

为了完成这里的故事,@scope 还允许确定样式作用域的结束位置。这一点无法通过任何命名惯例或预处理器来完成;这是一种特殊的做法,只有浏览器内置的 CSS 才能做到这一点。在以下示例中,当 .media-block 的子级是 .content 的同级或父级时,专门应用 img.content 样式:

@scope (.media-block) to (.content) {
  img {
    border-radius: 50%;
  }

  .content {
    padding: 1em;
  }
}

资源

没有 CSS 方式适用于砌体布局

在将 CSS 与网格融合之前,JavaScript 是实现砌体布局的最佳方式,因为任何包含列或 flexbox 的 CSS 方法都不能准确表示内容顺序。

将 CSS 与网格整合后,无需 JavaScript 库,内容顺序即可正确无误。

砌体布局的屏幕截图,其中显示数字沿顶部移动,然后向下移动。
Smashing Magazine 中的图片和演示
https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/

上述演示是通过以下 CSS 实现的:

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

令人欣喜的是,作为一种缺少布局策略的布局策略已经受到关注,您还可以立即在 Firefox 中试用此功能

资源

CSS 无法帮助用户减少数据

浏览器支持

  • x
  • x

来源

prefers-reduced-data 媒体查询之前,JavaScript 和服务器可以根据用户的操作系统或浏览器的“省流量”选项更改其行为,但 CSS 不能。

执行 prefers-reduced-data 媒体查询之后,CSS 可以加入用户体验增强功能,并在保存数据方面发挥自己的作用。

@media (prefers-reduced-data: reduce) {
  picture, video {
    display: none;
  }
}

此媒体滚动组件中使用了前面的 CSS,节省的费用可能会非常庞大。根据所访问视口的大小,加载网页所节省的开销越大。当用户与媒体滚动条互动时,系统会继续保存。这些图片均具有 loading="lazy" 属性,结合完全隐藏元素的 CSS,意味着永远不会发出对图片的网络请求。

电视节目轮播界面的屏幕截图,其中显示了许多缩略图和标题。

在我的测试中,在中等大小的视口上,最初加载了 40 个请求和 700kb 的资源。当用户滚动选择媒体时,系统会加载更多请求和资源。使用 CSS 和缩减数据媒体查询,可以加载 10 个请求和 172kb 的资源。这节省了半兆字节,用户甚至没有滚动过任何媒体,此时便不会再发出其他请求。

电视节目轮播界面的屏幕截图,其中显示了许多标题,但没有缩略图。

这种简化的数据体验不仅仅是节省流量,还有其他更多好处。可以看到更多标题,并且没有会分散注意力的封面图片。很多用户在浏览时都会使用流量节省程序模式,因为他们按兆字节 (MB) 数据付费。看到 CSS 能帮上忙,真的很开心。

资源

滚动贴靠功能过于有限

在滚动贴靠提案之前,如果您自行编写 JavaScript 来管理轮播界面、滑块或图库,则涉及所有的观察器和状态管理工作可能会迅速变得复杂。此外,如果不小心,自然滚动速度可能会被脚本标准化,让人感觉有点不自然,并且可能显得笨拙。

新增 API

snapChanging()

浏览器一释放 Snap 子项,此事件就会触发。这样一来,界面就可以反映滚动条缺少贴靠子项的情况以及滚动条的不确定贴靠状态,因为该状态正在使用中,并会跳转到新的位置。

document.querySelector('.snap-carousel').addEventListener('snapchanging', event => {
  console.log('Snap is changing', event.snappedTargetsList);
});
snapChanged()

一旦浏览器贴靠到新的子项且滚动条静止,此事件就会触发。这样一来,依赖于被贴靠的子项的任何界面都可以更新并反映连接。

document.querySelector('.snap-carousel').addEventListener('snapchanged', event => {
  console.log('Snap changed', event.snappedTargetsList);
});
scroll-start

滚动并不总是从一开始就开始。设想一下:向左滑动或向右滑动会触发不同的事件;考虑使用搜索栏,在网页加载时,搜索栏最初会处于隐藏状态,直到您滚动到顶部为止。借助此 CSS 属性,开发者可以指定滚动条应从特定点开始。

:root { --nav-height: 100px }

.snap-scroll-y {
  scroll-start-y: var(--nav-height);
}
:snap-target

此 CSS 选择器将与浏览器当前贴靠的滚动贴靠容器中的元素匹配。

.card {
  --shadow-distance: 5px;
  box-shadow: 0 var(--shadow-distance) 5px hsl(0 0% 0% / 25%);
  transition: box-shadow 350ms ease;
}

.card:snapped {
  --shadow-distance: 30px;
}

完成这些滚动贴靠提案后,制作滑块、轮播界面或图库会变得更加容易,因为浏览器现在为执行任务提供了便利,消除了观察者和滚动编排代码,取而代之的是使用内置 API。

这些 CSS 和 JS 功能仍处于早期开发阶段,但请留意有助于尽快采用和测试这些功能的 polyfill。

资源

在已知状态之间循环切换

toggle() 之前,只有浏览器内置的状态才能用于样式设置和交互。例如,复选框输入包含 :checked,它是内部管理的浏览器状态,CSS 能够使用该状态来直观地更改元素。

toggle() 之后,可以在任何元素上创建自定义状态,以便 CSS 更改和使用样式设置。它支持组、骑车、定向切换等。

以下示例实现了在完成时为列表项添加删除线的相同效果,但不包含任何复选框元素:

<ul class='ingredients'>
   <li>1 banana
   <li>1 cup blueberries
  ...
</ul>

以及相关的 CSS toggle() 样式:

li {
  toggle-root: check self;
}

li:toggle(check) {
  text-decoration: line-through;
}

如果您熟悉状态机,可能会注意到与 toggle() 存在多大的交叉差异。借助此功能,开发者能够将更多状态构建到 CSS 中,有望以更清晰、更有语义的方式编排互动和状态。

资源

自定义 select 元素

<selectmenu> 之前,CSS 无法使用丰富的 HTML 自定义 <option> 元素,也无法对选项列表的显示进行大幅更改。这导致开发者需要加载外部库,这些外部库重新创建了 <select> 的大部分功能,最终需要完成大量工作。

<selectmenu> 之后,开发者可以为选项元素提供丰富的 HTML,并视需要设置其样式,同时仍然满足无障碍功能要求并提供语义 HTML。

以下示例摘自 <selectmenu> 说明页面,创建了一个包含一些基本选项的新选择菜单:

<selectmenu>
  <option>Option 1</option>
  <option>Option 2</option>
  <option>Option 3</option>
</selectmenu>

CSS 可以定位元素的各个部分并为其设置样式:

.my-select-menu::part(button) {
  color: white;
  background-color: red;
  padding: 5px;
  border-radius: 5px;
}

.my-select-menu::part(listbox) {
  padding: 10px;
  margin-top: 5px;
  border: 1px solid red;
  border-radius: 5px;
}

一个带有红色强调色的精选菜单。

您可以在启用网页实验标志的情况下试用 Canary 版 Chromium 中的 <selectmenu> 元素。我们将在 2023 年及以后推出可自定义的选择菜单元素,敬请留意。

资源

将一个元素锚定到另一个元素

anchor() 之前,位置绝对和相对是为开发者提供的位置策略,可让子元素在父元素内移动。

anchor() 之后,开发者可以将元素定位到其他元素,而不管它们是否为子元素。此外,开发者还可以指定要根据哪条边定位,以及创建元素之间位置关系的其他注意事项。

如果您希望了解更多信息,解释器提供了一些很好的示例和代码示例。

资源