Google I/O 2023:最新 CSS 特性解读!

1,187 阅读14分钟

过去几个月迎来了 Web UI 的黄金时代。新的平台功能随着跨浏览器的广泛采用而落地,支持比以往更多的 Web 功能和自定义功能。

以下是最近推出或即将推出的 20 个最令人兴奋的 CSS 功能:

  • 容器查询
  • 样式查询
  • :has()
  • nth-of
  • text-wrap: balance
  • initial-letter
  • 动态视口单位
  • 广色域颜色空间
  • color-mix()
  • 嵌套
  • 级联层
  • 作用域样式
  • 三角函数
  • 单个变换属性
  • popover
  • 定位锚点
  • selectmenu
  • 离散属性过渡
  • 滚动驱动的动画
  • View transitions

新的响应式

容器查询

浏览器支持:

容器查询最近在所有现代浏览器中已经稳定。它允许查询父元素的大小和样式,以确定应应用于其任何子元素的样式。媒体查询只能访问和利用视口的信息,它是一种更精确的工具,可以支持任意数量的布局或嵌套布局。

在下面的收件箱示例中,主收件箱和收藏夹侧边栏都是容器。它们内部的电子邮件根据可用空间调整其网格布局,并根据需要显示或隐藏电子邮件时间戳。

因为有了容器查询,这些组件的样式都是动态的。 如果调整页面大小和布局,组件将响应它们单独分配的空间。 侧边栏变成了一个有更多空间的顶部栏,布局看起来更像主收件箱。当空间较少时,它们都以紧凑的格式显示。

样式查询

浏览器支持:

image.png

容器查询规范还允许查询父容器的样式。这目前在 Chrome 111 中部分实现,可以在其中使用 CSS 自定义属性来应用容器样式。以下示例使用存储在自定义属性值中的天气特征(例如下雨、晴天和多云)来设置卡片背景和指示器图标的样式。

@container style(--sunny: true) {
  .weather-card {
    background: linear-gradient(-30deg, yellow, orange);
  }

  .weather-card:after {
    content: url(<data-uri-for-demo-brevity>);
    background: gold;
  }
}

image.png

这只是样式查询的开始。将来,将使用布尔查询来确定自定义属性值是否存在并减少代码重复,目前正在讨论的是范围查询,以根据值的范围应用样式。这样就可以使用下雨或云层覆盖几率的百分比值来应用此处显示的样式。

:has()

浏览器支持:

image.png

:has() 选择器是现代浏览器中最强大的新 CSS 功能之一。 使用 :has()可以通过检查父元素是否包含特定子元素或这些子元素是否处于特定状态来应用样式,也就是父选择器。

在容器查询示例的基础上,可以使用 :has() 使组件更加动态。 其中,带有“⭐️”元素的项目会应用灰色背景,而带有选中复选框的项目会应用蓝色背景。

image.png

此 API 不限于父级选择,还可以为父级中的任何子级设置样式。 例如,当项目具有 ⭐️ 元素时,标题为粗体。 这是通过 .item:has(.star) .title 完成的。 使用 :has() 选择器可以访问父元素、子元素,甚至兄弟元素,这使它成为一个非常灵活的 API。

nth-of

浏览器支持:

image.png

Web 平台现在有更高级的第 n 个子元素选择。 高级 nth-child 语法提供了一个新关键字(“of”),它允许使用现有的 An+B 语法,并在其中搜索更具体的子集。

如果使用常规的 nth-child,例如 :nth-child(2) 在特殊类上,浏览器将选择应用了特殊类的元素,也是第二个子元素。 这与 :nth-child(2 of .special) 形成对比,后者将首先预过滤所有 .special 元素,然后从该列表中选择第二个。

:nth-child(2 of .highlight) {
	outline: 0.3rem dashed hotpink;
	outline-offset: 0.7rem;
}

image.png

text-wrap: balance

从 Chrome 114 开始,可以使用 text-wrap 属性并设置 balance 值,对标题使用文字自动换行平衡技术实现排版。

为了平衡文本,浏览器有效地对最小宽度执行二进制搜索,这不会导致任何额外的行,在一个 CSS 像素(不是显示像素)处停止。 为了进一步减少二进制搜索中的步骤,浏览器从平均线宽的 80% 开始。

initial-letter

initial-letter 属性可以更好地控制内嵌首字下沉样式。可以在:first-letter伪元素上使用initial-letter指定以下内容:基于字母占据的行数,来指定字母的大小。字母的块偏移量或“下沉”,来指定字母所在位置。

image.png

动态视口单位

浏览器支持:

image.png

Web开发人员面临的一个常见问题是在移动设备上实现准确和一致的全视口大小。作为开发者,希望100vh(视口高度的100%)指的是“与视口一样高”,但vh单位无法考虑到移动设备上缩回的导航栏之类的因素,因此有时它会太长并导致页面滚动。

image.png

为了解决这个问题,现在Web平台上有新的单位值,包括:

  • 小视口高度和宽度(svh和svw),表示最小活动视口大小。
  • 大视口高度和宽度(lvh和lvw),表示最大活动视口大小。
  • 动态视口高度和宽度(dvh和dvw)。

当额外的动态浏览器工具栏(例如顶部地址或底部选项卡栏)可见或不可见时,动态视口单位的值会发生变化。

广色域颜色空间

Web 平台的另一个重要新增功能是广色域色彩空间。 在 Web 平台上提供广色域颜色之前,可以拍出色彩鲜艳的照片,可以在现代设备上查看,但无法获得按钮、文本颜色或背景来匹配这些鲜艳的值。

现在在 Web 平台上有一系列新的颜色空间,包括 REC2020、P3、XYZ、LAB、OKLAB、LCH和OKLCH。

image.png

可以在 DevTools 中看到颜色范围是如何扩展的,那条白线标示出 sRGB 范围的结束位置和广色域颜色范围的开始位置。

image.png

color-mix()

浏览器支持:

image.png

color-mix() 函数支持混合两个颜色值,基于颜色通道创建新值。混合颜色的颜色空间会影响结果。在更感知的颜色空间(如oklch)中工作时,将经过与sRGB等不同的颜色范围。

color-mix(in srgb, blue, white);
color-mix(in srgb-linear, blue, white);
color-mix(in lch, blue, white);
color-mix(in oklch, blue, white);
color-mix(in lab, blue, white);
color-mix(in oklab, blue, white);
color-mix(in xyz, blue, white);

image.png

color-mix()函数提供了一个长期以来备受期待的功能:在保留不透明颜色值的同时,添加一些透明度。现在,可以在不同的不透明度下使用品牌颜色变量创建这些颜色的变体。方法是将颜色与透明混合。当将品牌颜色蓝色与10%透明度混合时,会得到一个90%不透明的品牌颜色。

image.png

现在可以在 Chrome DevTools 中看到这一点,在样式窗格中有一个非常漂亮的预览维恩图图标。

image.png

CSS基础

CSS 嵌套

浏览器支持:

image.png

CSS嵌套是Sass中很受欢迎的功能,也是多年来CSS开发人员最重要的需求之一,现在终于出现在 Web 平台上。嵌套允许开发人员以更简洁、分组的格式编写代码,减少冗余。

.card {}
.card:hover {}

/* 嵌套 */
.card {
  &:hover {
    
  }
}

我们还可以嵌套媒体查询,这也意味着可以嵌套容器查询。在下面的示例中,如果卡片容器足够宽度,卡片将从纵向布局更改为横向布局:

.card {
  display: grid;
  gap: 1rem;

  @container (width >= 480px) {
    display: flex;
  }
}

当容器有更多(或等于)480px 的可用行内空间时,就会发生对 flex 的布局调整。 当条件满足时,浏览器将简单地应用新的显示样式。

级联层

浏览器支持:

image.png

开发人员另一个痛点是确保样式胜过其他样式的一致性,解决这个问题的一个部分是更好地控制 CSS 级联。级联层通过让用户控制哪些层比其他层具有更高的优先级来解决这个问题,这意味着可以更好地控制何时应用样式。

image.png

image.png

作用域

浏览器支持:

image.png

CSS 作用域样式允许开发人员指定应用特定样式的边界,本质上是在 CSS 中创建原生命名空间。 以前,开发人员依靠第 3 方脚本来重命名类,或特定的命名约定来防止样式冲突,但很快,可以使用 @scope

这里将 .title 元素限定为 .card。 这将防止该 title 元素与页面上的任何其他 .title 元素发生冲突,例如博客文章标题或其他标题。

@scope (.card) {
  .title { 
    font-weight: bold;
  }
}

image.png

三角函数

浏览器支持:

CSS的另一个新功能是将三角函数添加到现有的CSS数学函数中。这些函数现在在所有现代浏览器中都是稳定的,并使您能够在Web平台上创建更有机的布局。一个很好的例子是这个径向菜单布局,现在可以使用sin()和cos()函数进行设计和动画。

在下面的示例中,点围绕中心点旋转。每个点不是围绕其自身的中心旋转然后向外移动,而是在 X 和 Y 轴上平移。 X 轴和 Y 轴上的距离分别通过考虑 --anglecos()sin() 来确定。

单个变换属性

过去,需要依赖变换函数来应用子函数来缩放、旋转和平移 UI 元素。这涉及大量重复工作,并且在动画中的不同时间应用多个变换时尤其令人沮丧。

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

.target:hover {
  transform: translateX(50%) rotate(30deg) scale(2); /* Only scale changed here, yet you have to repeat all other parts */
}

现在,可以通过分离变换类型并单独应用它们来在 CSS 动画中拥有所有这些细节。

.target {
  translate: 50% 0;
  rotate: 30deg;
  scale: 1.2;
}

.target:hover {
  scale: 2;
}

这样,平移、旋转或缩放的变化可以在动画期间的不同时间以不同的变化率同时发生。

可定制的组件

为了确保通过 Web 平台解决一些关键的开发人员需求,正在与 OpenUI 社区小组合作,并确定了三个解决方案:

  • 具有事件处理程序、声明性DOM结构和可访问默认值的内置弹出功能。
  • 用于将两个元素连接在一起以启用锚定定位的CSS API。
  • 可自定义下拉菜单组件,用于想要在选择框内部样式化内容的情况。

Popover

popover API 为元素提供了一些内置的浏览器支持,例如:

  • 支持顶层,因此不必管理 z-index。 当打开弹出窗或对话框时,将该元素提升到页面顶部的特殊层。
  • 当在元素外部单击时,弹出窗口将被关闭,从可访问性树中删除,并正确管理焦点。

这一切意味着可以编写更少的 JavaScript 来创建所有这些功能并跟踪所有这些状态。

image.png

popover 的 DOM 结构是声明性的,可以像给 popover 元素一个 idpopover 属性一样清晰地编写。 然后,将该 id 同步到将打开弹出窗的元素,例如具有 popovertarget 属性的按钮:

<div id="event-popup" popover>
  <!-- Popover 内容 -–>
</div>

<button popovertarget="event-popup">Create New Event</button> 

popoverpopover=auto 的简写。具有 popover=auto 的元素在打开时会强制关闭其他弹出窗口,并接收焦点,并且可以进行轻型取消操作。相反,popover=manual元素不会强制关闭任何其他元素类型,也不会立即接收焦点,并且不支持轻型取消。它们通过切换或其他关闭操作关闭。

锚点定位

弹出框也经常用在对话框和工具提示等元素中,这些元素通常需要锚定到特定元素。以这个事件为例。 当单击日历事件时,单击的事件附近会出现一个对话框。日历项是锚点,弹出窗口是显示事件详细信息的对话框。

我们可以使用 anchor() 函数创建一个居中的工具提示,使用锚点的宽度将工具提示定位在锚点 x 位置的 50% 处。 然后,使用现有的定位值来应用其余的放置样式。

但是,如果弹出窗口根据定位方式不适合视口,会发生什么情况?

image.png

为了解决这个问题,锚点定位 API 包含可以自定义的后备位置。以下示例创建了一个名为top-then-bottom的后备位置。 浏览器将首先尝试将工具提示定位在顶部,如果不适合视口,浏览器会将其定位在底部的锚定元素下方。

.center-tooltip {
  position-fallback: --top-then-bottom;
  translate: -50% 0;
}

@position-fallback --top-then-bottom {
  @try {
    bottom: calc(anchor(top) + 0.5rem);
    left: anchor(center);
  }

  @try {
    top: calc(anchor(bottom) + 0.5rem);
    left: anchor(center);
  }
}

<selectmenu>

通过弹出框和锚点定位,可以构建完全可定制的选择菜单。 OpenUI 社区小组一直在研究这些菜单的基本结构,并寻找允许自定义其中任何内容的方法。 以下图为例:

image.png

要构建最左侧的选择菜单示例,彩色点对应于日历事件中显示的颜色,可以按如下方式编写:

<selectmenu>
  <button slot="button" behavior="button">
    <span>Select event type</span>
    <span behavior="selected-value" slot="selected-value"></span>
    <span><img src="icon.svg"/></span>
  </button>
  <option value="meeting">
    <figure class="royalblue"></figure>
    <p>Meeting</p>
  </option>
  <option value="break">
    <figure class="gold"></figure>
     <p>Lunch/Break</p>
  </option>
  ...
</selectmenu>

离散属性过渡

为了使所有这些弹出式窗口平稳地切换进入和退出,Web 需要一些方法来为离散属性设置动画。作为为弹出窗、选择菜单甚至现有元素(如对话框或自定义组件)启用良好过渡的工作的一部分,浏览器正在用新的方式来支持这些动画。

下面的 popover 演示使用 :popover-open 来展示打开状态,@initial 展示打开之前的状态,并在开启后关闭状态下直接将一个transform值应用于元素。为使此功能与display一起使用,它需要添加到transition属性中,如下所示:

.settings-popover {
  &:popover-open {
    /*   0. before-change   */
    @initial {
      transform: translateY(20px);
      opacity: 0;
    }

    /*   1. open (changed) state   */
    transform: translateY(0);
    opacity: 1;
  }

  /*   2. After-change state */
  transform: translateY(-50px);
  opacity: 0;

  /*  enumarate transitioning properties, including display */
  transition: transform 0.5s, opacity 0.5s, display 0.5s;
}

交互

滚动驱动的动画

浏览器支持:

image.png

滚动驱动的动画允许根据滚动容器的滚动位置来控制动画的播放。 这意味着当向上或向下滚动时,动画会向前或向后滑动。 此外,使用滚动驱动的动画,还可以根据元素在其滚动容器中的位置来控制动画。这就可以创建有趣的效果,例如视差背景图像、滚动进度条和在进入视图时自动显示的图像。

此 API 支持一组 JavaScript 类和 CSS 属性,可以轻松创建声明式滚动驱动的动画。

要通过滚动来驱动 CSS 动画,可以使用新的 scroll-timelineview-timelineanimation-timeline 属性。要驱动 JavaScript Web 动画 API,可以将 ScrollTimelineViewTimeline 实例作为时间轴选项传递给 Element.animate()

这些新的 API 与现有的 Web Animations 和 CSS Animations API 结合使用,这意味着它们可以从这些 API 的优势中获益。这包括让这些动画脱离主线程运行的能力。现在可以拥有丝般流畅的动画,由滚动驱动,脱离主线程运行,只需几行额外的代码。

View transitions

浏览器支持:

View Transition API 可以轻松地一步更改 DOM,同时在两种状态之间创建动画转换。 这些可以是视图之间的简单淡入淡出,但也可以控制页面各个部分的过渡方式。

View Transitions 可以用作渐进式增强:将更新DOM的代码使用 View Transition API 包装起来,并为不支持该功能的浏览器提供回退方案。

function spaNavigate(data) {
  // Fallback for browsers that don't support this API:
  if (!document.startViewTransition) {
    updateTheDOMSomehow(data);
    return;
  }

  // With a transition:
  document.startViewTransition(() => updateTheDOMSomehow(data));
}

过渡应该是什么样子是通过 CSS 控制的:

@keyframes slide-from-right {
  from { opacity: 0; transform: translateX(75px); }
}

@keyframes slide-to-left {
  to { opacity: 0; transform: translateX(-75px); }
}

::view-transition-old(root) {
  animation: 350ms both slide-to-left ease;
}

::view-transition-new(root) {
  animation: 350ms both slide-from-right ease;
}

View Transitions 目前适用于 Chrome 111 中的单页应用 (SPA),多页应用支持正在开发中。

> 参考:[https://developer.chrome.com/blog/whats-new-css-ui-2023/](https://developer.chrome.com/blog/whats-new-css-ui-2023/)