用户偏好媒体功能客户端提示标头

一组客户端提示标头允许站点在请求时选择性地获取用户的媒体偏好,从而允许服务器为了性能原因内联正确的 CSS。

CSS 媒体查询,特别是用户偏好媒体功能(如 prefers-color-schemeprefers-reduced-motion),可能会对页面需要交付的 CSS 量以及用户在页面加载时的体验产生重大影响

专注于 prefers-color-scheme(但强调推理也适用于其他用户偏好媒体功能),最佳实践是在关键渲染路径中不加载特定不匹配配色方案的 CSS,而是最初仅加载当前相关的 CSS。一种方法是通过 <link media>

然而,像 Google 搜索 这样希望尊重用户偏好媒体功能(如 prefers-color-scheme)并出于性能原因内联 CSS 的高流量站点,需要在请求时理想地了解首选配色方案(或其他用户偏好媒体功能),以便初始 HTML 负载已经内联了正确的 CSS。

此外,特别是对于 prefers-color-scheme,站点无论如何都希望避免不准确颜色主题的闪烁

Sec-CH-Prefers-Color-SchemeSec-CH-Prefers-Reduced-Motion 客户端提示标头是旨在解决此问题的一系列用户偏好媒体功能客户端提示标头中的首批标头。

客户端提示背景知识

HTTP 客户端提示定义了一个 Accept-CH 响应标头,服务器可以使用该标头来声明其使用请求标头进行主动内容协商(俗称客户端提示)。用户偏好媒体功能客户端提示标头提案定义了一组旨在传达用户偏好媒体功能的客户端提示。这些客户端提示以它们报告的相应用户偏好媒体功能命名。例如,根据 prefers-color-scheme 的当前首选配色方案通过恰当命名的 Sec-CH-Prefers-Color-Scheme 客户端提示报告。

关键客户端提示背景知识

用户偏好媒体功能客户端提示标头中提出的客户端提示大概最常被用作关键客户端提示。关键客户端提示是能够有意义地更改结果资源的客户端提示。应该在页面加载(包括初始页面加载)中一致地获取此类资源,以避免出现明显的、用户可见的切换。

客户端提示语法

用户偏好媒体功能由名称(如 prefers-reduced-motion)和允许的值(如 no-preferencereduce)组成。每个客户端提示标头字段都表示为HTTP 结构化标头对象,其中包含一个项目,该项目的值是一个字符串。例如,为了传达用户偏好深色主题和减少的动画,客户端提示如下例所示。

GET / HTTP/2
Host: example.com
Sec-CH-Prefers-Color-Scheme: "dark"
Sec-CH-Prefers-Reduced-Motion: "reduce"

上述客户端提示中传达的信息的 CSS 等效项分别是 @media (prefers-color-scheme: dark) {}@media (prefers-reduced-motion: reduce) {}

客户端提示完整列表

客户端提示列表仿照Media Queries Level 5 中的用户偏好媒体功能

客户端提示 允许的值 对应的用户偏好媒体功能
Sec-CH-Prefers-Reduced-Motion no-preference, reduce prefers-reduced-motion
Sec-CH-Prefers-Reduced-Transparency no-preference, reduce prefers-reduced-transparency
Sec-CH-Prefers-Contrast no-preference, less, more, custom prefers-contrast
Sec-CH-Forced-Colors active, none forced-colors
Sec-CH-Prefers-Color-Scheme light, dark prefers-color-scheme
Sec-CH-Prefers-Reduced-Data no-preference, reduce prefers-reduced-data

浏览器支持

Sec-CH-Prefers-Color-Scheme 客户端提示标头在 Chromium 93 上受支持。Sec-CH-Prefers-Reduced-Motion 客户端提示标头在 Chromium 108 上受支持。其他供应商的反馈(即 WebKitMozilla 的反馈)正在等待中。

Sec-CH-Prefers-Color-Scheme 演示

在 Chromium 93 中尝试演示,并注意内联 CSS 如何根据用户的首选配色方案而变化。

Sec-CH-Prefers-Color-Scheme: dark

Sec-CH-Prefers-Color-Scheme: light

Sec-CH-Prefers-Reduced-Motion 演示

在 Chromium 108 中尝试演示,并注意内联 CSS 如何根据用户的动画偏好而变化。

工作原理

  1. 客户端向服务器发出初始请求。bash GET / HTTP/2 Host: example.com
  2. 服务器响应,通过 Accept-CH 告知客户端,它接受 Sec-CH-Prefers-Color-SchemeSec-CH-Prefers-Contrast 客户端提示,其中根据 Critical-CH,它认为 Sec-CH-Prefers-Color-Scheme 是一个关键客户端提示,它也会根据 Vary 传达的提示来改变响应。bash HTTP/2 200 OK Content-Type: text/html Accept-CH: Sec-CH-Prefers-Color-Scheme, Sec-CH-Prefers-Contrast Vary: Sec-CH-Prefers-Color-Scheme Critical-CH: Sec-CH-Prefers-Color-Scheme
  3. 然后,客户端重试请求,通过 Sec-CH-Prefers-Color-Scheme 告知服务器,它具有对深色主题内容的用户偏好。bash GET / HTTP/2 Host: example.com Sec-CH-Prefers-Color-Scheme: "dark"
  4. 然后,服务器可以相应地定制对客户端偏好的响应,例如,将负责深色主题的 CSS 内联到响应正文中。

Node.js 示例

下面为流行的 Express.js 框架编写的 Node.js 示例展示了在实践中处理 Sec-CH-Prefers-Color-Scheme 客户端提示标头可能是什么样子。此代码实际上为上面的演示提供支持。

app.get("/", (req, res) => {
  // Tell the client the server accepts the `Sec-CH-Prefers-Color-Scheme` client hint…
  res.set("Accept-CH", "Sec-CH-Prefers-Color-Scheme");
  // …and that the server's response will vary based on its value…
  res.set("Vary", "Sec-CH-Prefers-Color-Scheme");
  // …and that the server considers this client hint a _critical_ client hint.
  res.set("Critical-CH", "Sec-CH-Prefers-Color-Scheme");
  // Read the user's preferred color scheme from the headers…
  const prefersColorScheme = req.get("sec-ch-prefers-color-scheme");
  // …and send the adequate HTML response with the right CSS inlined.
  res.send(getHTML(prefersColorScheme));
});

隐私和安全注意事项

Chromium 团队使用 控制对强大的 Web 平台功能的访问中定义的核心原则(包括用户控制、透明度和人体工程学)设计和实施了用户偏好媒体功能客户端提示标头。

HTTP 客户端提示的安全注意事项客户端提示可靠性的安全注意事项同样适用于此提案。

参考资料

致谢

非常感谢 Yoav Weiss 提供的宝贵反馈和建议。题图作者:Tdadamemd,来自 Wikimedia Commons