2022 年 CSS 现状

2022 年 Google IO 大会上展示的目前和未来的网页样式设置功能,以及一些额外内容。

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 工作的影响。

级联层

浏览器支持

  • Chrome:99.
  • Edge:99.
  • Firefox:97。
  • Safari:15.4。

来源

@layer 之前,加载的样式表的发现顺序非常重要,因为最后加载的样式可以覆盖之前加载的样式。这导致了精心管理的条目样式表,开发者需要先加载不太重要的样式,然后再加载更重要的样式。有各种方法(如 ITCSS)可帮助开发者管理这种重要性。

借助 @layer,条目文件可以提前预定义图层及其顺序。然后,随着样式的加载、加载或定义,它们可以放置在图层中,从而保留样式替换重要性,而无需精心管理加载编排。

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

Chrome 开发者工具有助于直观地了解哪些样式来自哪些层:

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

资源

子网格

浏览器支持

  • Chrome:117.
  • Edge:117.
  • Firefox:71。
  • Safari: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 定义的线条。

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

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

资源

容器查询

浏览器支持

  • Chrome:105。
  • Edge:105.
  • Firefox:110。
  • Safari: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

浏览器支持

  • Chrome:93。
  • Edge:93.
  • Firefox:92.
  • Safari:15.4。

来源

accent-color 之前,如果您希望表单采用与品牌相匹配的颜色,最终可能会使用复杂的库或 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

在过去几十年中,Web 一直由 sRGB 主导,但在不断扩大的数字世界中,高清显示屏和预装 OLED 或 QLED 屏幕的移动设备越来越多,sRGB 已不够用。此外,动态页面应能根据用户偏好进行自适应,而颜色管理已成为设计师、设计系统和代码维护人员日益关注的问题。

不过在 2022 年还不是 - CSS 具有许多新的颜色功能和空间: - 达到显示屏高清颜色功能的颜色。 - 与 intent 匹配的颜色空间,例如感知一致性。- 会极大地改变插值结果的渐变色彩空间。 - 色彩函数可帮助您混合和对比颜色,并选择您要进行工作的空间。

在所有这些颜色功能之前,设计系统需要预计算适当的对比色,并确保色板的色彩饱和度适当,同时预处理器或 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()

浏览器支持

  • Chrome:101.
  • Edge:101。
  • Firefox:96.
  • Safari:15。

来源

HWB 表示色相、白度和黑度。它以人性化的方式呈现颜色,因为它只是一个色调以及用于调亮或调暗的白色或黑色量。将颜色与白色或黑色混合在一起的音乐人可能会发现自己很喜欢这种附加的颜色语法。

使用此颜色函数会产生 sRGB 颜色空间中的颜色,与 HSL 和 RGB 相同。就 2022 年的新情况而言,虽然这不会给您带来新的颜色,但可能会让语法和心智模式的爱好者更轻松地完成一些任务。

资源

色彩空间

颜色的表示方式是通过颜色空间实现的。每种色彩空间都提供各种功能和权衡,以便处理颜色。有些人可能会将所有亮色放在一起;有些人可能会先按亮度排列。

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

color-mix()

浏览器支持

  • Chrome:111.
  • Edge:111.
  • Firefox:113.
  • Safari: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()

浏览器支持

  • Chrome:不支持。
  • Edge:不支持。
  • Firefox:不受支持。
  • Safari:背后有旗帜。

来源

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 语言本身内置选择合适对比色功能后,实现无障碍且易于阅读的界面会变得多么容易。

资源

相对颜色语法

浏览器支持

  • Chrome:111.
  • Edge:111.
  • Firefox:113.
  • Safari: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

浏览器支持

  • Chrome:102。
  • Edge:102.
  • Firefox:112.
  • Safari:15.5。

来源

inert 之前,最好将用户的注意力引导到需要立即关注的网页或应用区域。这种引导式焦点策略后来被称为“焦点捕获”,因为开发者会将焦点放置在交互空间中,监听焦点变化事件,如果焦点离开交互空间,则会被强制拉回。系统会引导使用键盘或屏幕阅读器的用户返回到 Interactive Space,以确保任务已完成,然后再继续操作。

inert 之后,无需进行任何捕获,因为您可以冻结或保护网页或应用的整个部分。当文档的这些部分处于不活跃状态时,点击和焦点更改尝试根本无法执行。您也可以将其视为守卫,而不是陷阱,在这种情况下,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 字体之后,网页占用的空间更小,并且支持矢量可缩放、可重新定位、支持渐变效果和混合模式的字体,这些字体接受参数来按用例自定义字体或匹配品牌。

对比可视化和条形图,显示 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;
}
显示 Bungee Spice 字体和“DUNE”一词的屏幕截图。
显示了自定义颜色的 Bungee Spice 字体,来源:https://developer.chrome.com/blog/colrv1-fonts/

随着越来越多可变字体和彩色字体的推出,Web 排版正在朝着丰富的自定义和富有创意的表达方式迈进。

资源

视口单位

一张图表,显示了设备屏幕、浏览器窗口和 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()

浏览器支持

  • Chrome:105。
  • Edge:105.
  • Firefox:121。
  • Safari:15.4。

来源

:has() 之前,选择器主体始终位于末尾。例如,此选择器的主体是列表项 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> 时调整图片的样式。在以下示例中,系统会选择包含 figcaption 的图,然后选择该上下文中的图片。使用 :has() 不会更改主体,因为我们定位的主体是图片,而不是图形:

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

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

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

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

资源

2022 年及之后

2022 年推出所有这些出色的功能后,我们仍有许多工作要做。下一部分将介绍一些尚未解决的问题,以及正在积极开发的解决方案。这些解决方案目前处于实验阶段,即使它们在浏览器中可以通过标志指定或使用。

下一部分的结果应该令人欣慰,因为列出的问题会让许多公司的许多人在寻求解决方案,而不是这些解决方案将在 2023 年发布。

类型较为宽松的自定义属性

浏览器支持

  • Chrome:85.
  • Edge:85。
  • Firefox:128.
  • Safari: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/Option 键时设置鼠标 x 和 y 坐标,然后将 focal-size 更改为较小的值(例如 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 有助于整体上改善样式组织、集中化和所有权。组件可以对自己的样式进行分组和自定义,而不是将其分散在其他样式块中。这些示例看起来可能很小,但出于方便和易读的考虑,它可能会产生非常大的影响。

资源

限定样式非常困难

浏览器支持

  • Chrome:118。
  • Edge:118。
  • Firefox:背后有旗帜。
  • Safari: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 无法帮助用户减少数据流量

浏览器支持

  • Chrome:需要启用标志。
  • Edge:需要切换标志才能启用。
  • Firefox:不受支持。
  • Safari:不支持。

来源

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 的资源。这相当于节省了 512 KB,而用户甚至还没有滚动任何媒体,此时系统也没有发出其他请求。

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

这种减少数据流量的体验不仅仅能节省数据,还有更多优势。可以看到更多标题,并且没有分散注意力的封面图片来吸引用户的注意力。许多用户会使用流量节省程序模式浏览内容,因为他们按兆字节 (MB) 的流量付费。看到 CSS 能帮上忙,真是太棒了。

资源

滚动贴靠功能过于有限

在这些滚动贴靠提案之前,如果您要编写自己的 JavaScript 来管理轮播界面、滑块或图库,由于需要处理所有观察器和状态管理,因此会很快变得复杂。此外,如果不小心,自然滚动速度可能会被脚本标准化,导致用户互动感觉不自然,甚至可能很笨拙。

新增 API

snapChanging()

浏览器释放固定子项后,此事件便会触发。这样,界面就可以反映缺少贴靠子项以及滚动条的不确定贴靠状态,因为滚动条正在使用中,并将在新的某个位置打开。

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;
}

一个采用红色强调色的精选菜单。

您可以在启用了 Web 实验标志的 Canary 版 Chromium 中试用 <selectmenu> 元素。请留意 2023 年及之后推出的可自定义选择菜单元素。

资源

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

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

anchor() 之后,开发者可以将元素放置到其他元素中,无论这些元素是否为子元素。它还允许开发者指定要对齐到哪个边缘,以及用于在元素之间创建位置关系的其他实用功能。

如果您有兴趣了解详情,该说明文档提供了一些非常棒的示例和代码示例。

资源