Google IO 2022 上展示的目前和未来的网页样式设置功能,以及一些额外功能。
无论是功能还是合作浏览器功能发布,2022 年都将是 CSS 最重要的一年,我们的共同目标是实现 14 项功能!
概览
此博文是 2022 年 Google IO 大会上演讲的文章形式。本文并非旨在深入介绍每项功能,而是提供简要的介绍和概览,以激发您的兴趣,提供广度而非深度。如果您感兴趣,请查看相应部分末尾的资源链接,了解更多信息。
目录
您可以使用以下列表跳转到感兴趣的主题:
浏览器兼容性
许多 CSS 功能之所以被设置为协同发布,主要是因为 Interop 2022 的努力。在研究互操作性工作之前,请务必先了解 Compat 2021 的工作。
Compat 2021
在 2021 年,我们根据开发者通过调查问卷提供的反馈,制定了以下目标:稳定当前功能、改进测试套件,并提高浏览器在以下五个功能方面的通过分数:
sticky
定位aspect-ratio
规模需求- “
flex
”布局 - “
grid
”布局 transform
定位和动画
测试得分全面提升,表明稳定性和可靠性有所提高。热烈祝贺以上团队!
Interop 2022
今年,浏览器厂商齐聚一堂,讨论了他们打算开发的功能和优先级,共同努力。他们计划为开发者提供以下 Web 功能:
@layer
- 颜色空间和函数
- contain 属性
<dialog>
- 表单兼容性
- 滚动
- 子网格
- 排版
- 视口单位
- 网络兼容性
这是一个令人兴奋且雄心勃勃的列表,我迫不及待地想看到这些项目取得进展。
2022 年新推出的功能
毫不意外,CSS 2022 的状态受到了 Interop 2022 工作的巨大影响。
级联层
在 @layer
之前,已加载样式表的发现顺序非常重要,因为最后加载的样式可以覆盖之前加载的样式。这导致了精心管理的入口样式表,开发者需要先加载不太重要的样式,然后再加载更重要的样式。目前有许多方法可帮助开发者管理这种重要性,例如 ITCSS。
借助 @layer
,入口文件可以预先定义图层及其顺序。然后,随着样式加载、被加载或被定义,它们可以放置在图层中,从而保留样式替换的重要性,但无需精心管理加载编排。
该视频展示了如何通过定义的级联层实现更自由、更随性的创作和加载过程,同时仍可根据需要保持级联。
Chrome 开发者工具有助于直观了解哪些样式来自哪些层:
资源
子网格
在 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>
的子级可以使用 fullbleed
和 main
列和行来对齐或调整自身大小。
.main-content {
grid-column: main;
}
.fullbleed {
grid-column: fullbleed;
}
开发者工具可帮助您查看网格线和子网格(目前仅限 Firefox)。 在下图中,父网格和子网格已叠加。现在,它与设计师最初考虑的布局类似。
在开发者工具的“元素”面板中,您可以查看哪些元素是网格和子网格,这对于调试或验证布局非常有帮助。

资源
容器查询
在 @container
之前,网页的元素只能响应整个视口的大小。这对于宏布局非常有用,但对于微布局(其外部容器不是整个视口)而言,布局无法相应地进行调整。
在 @container
之后,元素可以响应父容器的大小或样式!
唯一的注意事项是,容器必须声明自己是可能的查询目标,这是一个小要求,但能带来很大的好处。
/* establish a container */
.day {
container-type: inline-size;
container-name: calendar-day;
}
正是这些样式使得以下视频中的“周一”“周二”“周三”“周四”和“周五”列能够通过事件元素进行查询。
以下 CSS 用于查询 calendar-day
容器的大小,然后调整布局和字体大小:
@container calendar-day (max-width: 200px) {
.date {
display: block;
}
.date-num {
font-size: 2.5rem;
display: block;
}
}
以下是另一个示例:一个图书组件会根据拖动到的列中的可用空间调整自身大小:
Una 对情况的评估是正确的,即新的自适应。使用 @container
时,您可以做出许多令人兴奋且有意义的设计决策。
资源
- 容器查询规范
- 容器查询说明
- MDN 上的容器查询
- web.dev 上的新响应式设计
- Una 的日历演示
- 出色的容器查询集合
- 我们如何在 web.dev 上打造 Designcember
- Ahmad Shadeed:向 CSS 容器查询问好
accent-color
在 accent-color
之前,如果您想要制作具有品牌匹配颜色的表单,最终可能会得到复杂的库或 CSS 解决方案,这些方案随着时间的推移会变得难以管理。虽然他们为您提供了所有选项,并且希望包含无障碍功能,但选择使用内置组件还是采用自己的组件会变得很麻烦。
在 accent-color
之后,只需一行 CSS 即可将品牌颜色应用到内置组件。除了色调之外,浏览器还会为组件的辅助部分智能选择合适的对比色,并适应系统颜色方案(浅色或深色)。
/* tint everything */
:root {
accent-color: hotpink;
}
/* tint one element */
progress {
accent-color: indigo;
}
如需详细了解 accent-color
,请参阅我在 web.dev 上发布的博文,其中探讨了这一实用 CSS 属性的更多方面。
资源
颜色级别 4 和 5
在过去几十年中,网络一直以 sRGB 为主,但在高清显示屏和预装 OLED 或 QLED 屏幕的移动设备不断涌现的数字世界中,sRGB 已无法满足需求。此外,人们还期望动态网页能够适应用户偏好,而色彩管理也越来越受到设计师、设计系统和代码维护人员的关注。
不过,在 2022 年,CSS 新增了许多颜色函数和空间:- 可充分利用显示屏 HD 颜色功能的颜色。 - 与意图匹配的色彩空间,例如感知均匀性。 - 适用于渐变的色彩空间,可大幅改变插值结果。 - 颜色函数,可帮助您混合和对比颜色,并选择在哪个空间中进行操作。
在推出所有这些颜色功能之前,设计系统需要预先计算合适的对比色,并确保调色板具有适当的鲜艳度,而预处理器或 JavaScript 则负责完成繁重的工作。
有了所有这些颜色功能,浏览器和 CSS 就可以动态地及时完成所有工作。CSS 可以进行编排和计算,而无需向用户发送大量 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()
HWB 代表色调、白度和黑度。它以一种便于理解的方式来表达颜色,因为只需要指定色调以及用于调亮或调暗的白色或黑色量即可。如果艺术家经常将颜色与白色或黑色混合,可能会发现此颜色语法新增功能非常实用。
使用此颜色函数会生成 sRGB 颜色空间中的颜色,与 HSL 和 RGB 相同。就 2022 年的新功能而言,这不会为您提供新颜色,但可能会让语法和思维模式的粉丝更轻松地完成某些任务。
资源
色彩空间
颜色的表示方式是通过色彩空间实现的。每种色彩空间都提供不同的色彩处理功能,并具有不同的优缺点。有些人可能会将所有鲜艳的颜色放在一起;有些人可能会先根据亮度将它们排成一行。
2022 年 CSS 将提供 10 个新的色彩空间,每个色彩空间都具有独特的功能,可帮助设计师和开发者显示、选择和混合颜色。以前,sRGB 是处理颜色的唯一选择,但现在 CSS 带来了新的潜力和新的默认颜色空间 LCH。
color-mix()
在 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()
在 color-contrast()
之前,样式表作者需要提前了解无障碍颜色。通常,调色板会在色样上显示黑色或白色文字,以向色彩系统的用户指示需要哪种文字颜色才能与该色样形成适当的对比度。

在 color-contrast()
之后,样式表作者可以将任务完全交给浏览器。您不仅可以利用浏览器自动选择黑色或白色,还可以向其提供一系列适合设计系统的颜色,并让其选择第一个达到所需对比度的颜色。
以下是 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 语言本身中内置了选择适当对比色的功能,交付无障碍且清晰易读的界面将变得多么容易。
资源
相对颜色语法
在相对颜色语法出现之前,若要计算颜色并进行调整,需要将颜色通道单独放置到自定义属性中。此限制也使得 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()
来确保标签具有适当的对比度:

此函数还可用于生成调色板。以下演示展示了如何根据提供的基色生成整个调色板。这组 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);
}

希望您现在已经了解,根据色彩空间和不同颜色函数的优势和劣势,它们可以用于不同的用途。
资源
渐变颜色空间
在出现梯度颜色空间之前,sRGB 是使用的默认颜色空间。sRGB 通常可靠,但确实存在一些缺点,例如灰色死区。
在渐变颜色空间之后,告知浏览器要使用哪种颜色空间进行颜色插值。这样,开发者和设计者就可以选择自己喜欢的渐变效果。默认颜色空间也会更改为 LCH,而不是 sRGB。
新增的语法位于渐变方向之后,使用新的 in
语法,并且是可选的:
background-image: linear-gradient(
to right in hsl,
black, white
);
background-image: linear-gradient(
to right in lch,
black, white
);
以下是从黑色到白色的基本渐变效果。查看每个色彩空间中的结果范围。有些会比其他更早达到深黑色,有些则会过晚褪色为白色。
在下一个示例中,黑色会过渡到蓝色,因为它是渐变的已知问题空间。大多数色彩空间在颜色插值期间或(正如我喜欢的那样)在颜色从色彩空间内的 A 点移动到 B 点时,会逐渐变成紫色。由于渐变将从点 A 到点 B 沿直线变化,因此色彩空间的形状会大幅改变路径沿途经过的色标。
如需更深入地了解相关信息,并查看示例和评论,请参阅此 Twitter 帖子。
资源
inert
在 inert
之前,引导用户将注意力集中在需要立即关注的网页或应用区域是一种不错的做法。这种引导式焦点策略被称为焦点捕获,因为开发者会将焦点放置在互动空间中,监听焦点更改事件,如果焦点离开互动空间,则会强制将其移回。使用键盘或屏幕阅读器的用户会被引导回到互动空间,以确保在继续操作之前完成任务。
在 inert
之后,无需再进行捕获,因为您可以冻结或保护网页或应用的整个部分。当文档的这些部分处于非活动状态时,点击和焦点更改尝试将无法进行。也可以将此视为守卫而不是陷阱,其中 inert
并不想让您留在某个地方,而是让其他地方无法使用。
一个很好的例子是 JavaScript 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 字体出现之前,网络上已有 OT-SVG 字体,这也是一种开放式字体格式,支持渐变效果以及内置颜色和效果。 不过,这些气泡可能会变得非常大,虽然允许编辑文字,但自定义空间不大。
继 COLRv1 字体之后,Web 又推出了占用空间更小、可缩放矢量、可重新定位、具有渐变效果且支持混合模式的字体,这些字体可接受参数来根据使用情形或品牌要求自定义字体。

以下是 Chrome 开发者博客中关于表情符号的一篇博文中的示例。您可能已经注意到,如果放大表情符号的字号,表情符号会变得模糊不清。它是图片,而不是矢量图。在应用中,如果使用了表情符号,通常会将其替换为更高质量的资源。借助 COLRv1 字体,表情符号将以矢量形式呈现,效果精美:
图标字体可以利用此格式实现一些令人惊叹的功能,例如提供自定义双色调调色板等。加载 COLRv1 字体与加载任何其他字体文件一样:
@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);
自定义 COLRv1 字体是通过 @font-palette-values
完成的,这是一种特殊的 CSS at-rule,用于将一组自定义选项分组并命名为一个软件包,以便日后引用。请注意,您指定自定义名称的方式与指定自定义属性的方式相同,都是以 --
开头:
@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;
}

随着越来越多的可变字体和彩色字体可供使用,网页排版正朝着丰富的自定义和创意表达方向发展。
资源
视口单位
在推出新的视口变体之前,Web 提供了一些物理单位来帮助调整视口大小。一个用于高度、宽度、最小尺寸 (vmin) 和最长边 (vmax)。这些方法在许多方面都非常有效,但移动浏览器引入了复杂性。
在移动设备上加载网页时,系统会显示包含网址的状态栏,而此状态栏会占用部分视口空间。在几秒钟后,经过一些互动,状态栏可能会滑走,以便为用户提供更大的视口体验。但当该栏滑出时,视口高度会发生变化,任何 vh
单位都会随着目标尺寸的变化而发生位移和调整大小。
在后来的几年中,vh
单元需要专门决定要使用哪种视口大小,因为这会在移动设备上造成令人不适的视觉布局问题。我们确定 vh
将始终表示最大的视口。
.original-viewport-units {
height: 100vh;
width: 100vw;
--size: 100vmin;
--size: 100vmax;
}
在推出新的视口变体(小型、大型和动态视口单位)后,我们还添加了与物理视口单位对应的逻辑等效单位。其目的是让开发者和设计师能够根据具体场景选择要使用的单位。也许当状态栏消失时,出现轻微的布局突变是可以接受的,这样就可以放心地使用 dvh
(动态视口高度)。
以下是新视口变体提供的所有新视口单位选项的完整列表:
.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()
在 :has()
之前,选择器的正文始终位于末尾。例如,此选择器的对象是列表项:ul > li
。伪选择器可以更改选择器,但不会更改正文:ul > li:hover
或 ul >
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>
时调整图片的样式。在以下示例中,系统先选择带有 figcaption 的 figure,然后选择该上下文中的图片。使用 :has()
,不会更改主题,因为我们定位的主题是图片,而不是图形:
figure:has(figcaption) img {...}
组合似乎有无限可能。将 :has()
与数量查询相结合,并根据子元素的数量调整 CSS 网格布局。将 :has()
与互动伪类状态相结合,以全新的创意方式创建响应式应用。
借助 @supports
及其 selector()
函数,您可以轻松检查浏览器是否支持该语法,然后再使用它:
@supports (selector(:has(works))) {
/* safe to use :has() */
}
资源
2022 年及以后
不过,在 2022 年推出所有这些出色功能后,仍有许多事情难以实现。下一部分将介绍一些剩余的问题,以及正在积极开发以解决这些问题的方案。这些解决方案仍处于实验阶段,即使它们可能在浏览器中通过标志指定或提供。
接下来各部分的内容应让您放心,因为许多公司中的许多人都在寻求解决所列问题的方法,而不是说这些解决方案将在 2023 年发布。
松散类型的自定义属性
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>
。这意味着,即使向某个自定义属性提供了无效值,影子仍会继续正常运行。它不会失败,而是会恢复为 initial-value
0px
。
动画
除了类型安全之外,它还为动画打开了许多大门。CSS 语法的灵活性使得某些效果(例如渐变)无法实现动画效果。@property
在这里很有用,因为类型化 CSS 属性可以告知浏览器开发者在其他过于复杂的插值中的意图。它在很大程度上限制了可能性范围,以至于浏览器可以为以前无法添加动画效果的样式方面添加动画效果。
请看以下演示示例,其中使用径向渐变来制作叠加层的一部分,从而创建聚光灯聚焦效果。当按下 Alt/Opt 键时,JavaScript 会设置鼠标 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
,可以单独为某个属性设置类型并添加动画效果,浏览器可以轻松理解意图。
使用此聚焦效果的视频游戏始终会以动画形式显示圆圈,从大圆圈到针孔圆圈。以下是如何将 @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-width
或 max-width
在媒体查询范围出现之前,CSS 媒体查询使用 min-width
和 max-width
来表达大于和小于条件。具体可能如下所示:
@media (min-width: 320px) {
…
}
在媒体查询范围之后,同一媒体查询可能如下所示:
@media (width >= 320px) {
…
}
同时使用 min-width
和 max-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),而是可以留在文章的上下文中,并查看文章中包含的链接。关系和样式意图捆绑在一起,因此 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
有助于以更健康的方式组织、集中和管理样式。组件可以对自己的样式进行分组和拥有,而不是将它们分散在其他样式块中。在这些示例中,边距可能看起来很小,但它对便利性和可读性都有非常大的影响。
资源
确定样式的作用域非常困难
在 @scope
之前,由于 CSS 中的样式会层叠、继承,并且默认情况下具有全局作用域,因此存在许多策略。CSS 的这些功能在许多方面都非常方便,但对于复杂的网站和应用(可能包含许多不同样式的组件),级联的全局空间和性质可能会导致样式感觉像是在泄漏。
在 @scope
之后,样式不仅可以限定在某个上下文(例如类)内,还可以明确指定样式的结束位置,从而避免继续级联或继承。
在以下示例中,BEM 命名惯例范围可以反转为实际意图。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 库,内容顺序也会正确。

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 无法帮助用户减少数据用量
在 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 和 reduced-data 媒体查询后,系统会加载 10 个请求和 172 KB 的资源。这样一来,可节省 0.5 MB 的流量,而且用户甚至还没有滚动浏览任何媒体,此时不会再发出任何其他请求。
这种精简数据体验的优势不仅仅在于节省流量。这样一来,用户可以查看更多影视内容,而且不会被分散注意力的封面图片所干扰。许多用户以流量节省模式浏览,因为他们按兆字节流量付费,因此看到 CSS 能够在此处提供帮助,真是太棒了。
资源
- prefers-reduced-data 规范
- MDN 上的 prefers-reduced-data
- GUI 中的 prefers-reduced-data 挑战
- Smashing Magazine:改进核心网页指标 - Smashing Magazine 案例研究
滚动吸附功能过于有限
在这些滚动贴靠提案之前,编写自己的 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 中,从而以更清晰、更语义化的方式编排互动和状态。
资源
自定义选择元素
在 <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>
元素,前提是已启用 Web 实验标志。在 2023 年及以后,您将可以自定义选择菜单元素。
资源
将一个元素锚定到另一个元素
在 anchor()
之前,绝对定位和相对定位是为开发者提供的定位策略,用于让子元素在父元素内移动。
在 anchor()
之后,开发者可以将元素定位到其他元素,无论这些元素是否为子元素。它还允许开发者指定要定位的边缘,以及用于在元素之间创建位置关系的其他实用功能。
如果您有兴趣了解详情,可以查看该说明中的一些精彩示例和代码示例。