使用 CSS 阅读流实现逻辑顺序的焦点导航

发布时间:2025 年 5 月 1 日

CSS reading-flowreading-order 属性从 Chrome 137 开始提供。本文介绍了这些属性的设计背后的原因,以及一些简要详细信息,以帮助您开始使用这些属性。

网格和 flex 等布局方法彻底改变了前端开发,但它们的灵活性可能会给某些用户带来问题。很容易出现视觉顺序与 DOM 树中的源顺序不一致的情况。由于浏览器会遵循此来源顺序,因此如果您使用键盘浏览网站,部分用户可能会在浏览网页时遇到意外跳转。

我们设计并添加了 reading-flowreading-order 属性到 CSS 显示规范中,以尝试解决这个长期存在的问题。

reading-flow

reading-flow CSS 属性用于控制 Flex、网格或块布局中的元素向无障碍功能工具显示的顺序,以及使用线性顺序导航方法将焦点移至这些元素的方式。

它接受一个关键字值,默认值为 normal,用于保持按 DOM 顺序排列元素的行为。如需在 flex 容器中使用它,请将其值设为 flex-visualflex-flow。如需在网格容器中使用它,请将其值设为 grid-rowsgrid-columnsgrid-order

reading-order

借助 reading-order CSS 属性,您可以手动替换阅读流容器中内容的顺序。如需在网格、Flex 或块容器中使用此属性,请将容器上的 reading-flow 值设置为 source-order,并将各个项的 reading-order 设置为整数值。

Flexbox 中的示例

例如,您可能有一个 Flex 布局容器,其中包含三个元素,并且这些元素的排列顺序是从上到下,而您还想使用 order 属性重新排列该顺序。

<div class="box">
 <a href="#">One</a>
 <a href="#">Two</a>
 <a href="#">Three</a>
</div>
.box {
  display: flex;
  flex-direction: row-reverse;
}

.box :nth-child(1) {
  order: 2;
}

您可以尝试使用 TAB 键查找下一个可聚焦元素,使用 TAB+SHIFT 键查找上一个可聚焦元素,以便在这些元素之间导航。这遵循来源顺序:One、Two、Three。

从最终用户的角度来看,这没有意义,而且可能会造成很大的困惑。如果我们使用无障碍空间导航工具在页面中导航,也会发生同样的情况。

如需解决此问题,请设置 reading-flow 属性:

.box {
  reading-flow: flex-visual;
}

焦点顺序现在为:一、三、二。这与您从左到右阅读英语时获得的视觉顺序相同。

相反,如果您希望保持原始焦点顺序(即按相反顺序),可以设置:

.box {
  reading-flow: flex-flow;
}

焦点顺序现在是相反的 flex 顺序:Two、Three、One。在这两种情况下,系统都会考虑 CSS order 属性。

网格布局示例

如需了解此功能在网格中的运作方式,假设您要创建一个布局,其中包含 12 个可聚焦区域的 CSS 网格自动放置项。

<div class="wrapper">
 <a href="#">One</a>
 <a href="#">Two</a>
 <a href="#">Three</a>
 <a href="#">Four</a>
 <a href="#">Five</a>
 <a href="#">Six</a>
 <a href="#">Seven</a>
 <a href="#">Eight</a>
 <a href="#">Nine</a>
 <a href="#">Ten</a>
 <a href="#">Eleven</a>
 <a href="#">Twelve</a>
</div>

您希望第五个子项占据最顶部的最大空间,其次是位于网格中间的第二个子项。所有其他子项都可以按照列模板自动放置在网格中。

.wrapper {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 100px;
}
.wrapper a:nth-child(2) {
  grid-column: 3;
  grid-row: 2 / 4;
}
.wrapper a:nth-child(5) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}

尝试使用 Tab 键查找下一个可聚焦元素,使用 Tab+Shift 键查找上一个可聚焦元素。此属性遵循来源顺序:一到十二。

如需解决此问题,请设置 reading-flow 属性:

.wrapper {
  reading-flow: grid-rows;
}

焦点顺序现为:五、一、三、二、四、六、七、八、九、十、十一、十二。它遵循视觉顺序,逐行显示。

如果您想让阅读流程遵循列顺序,则可以改用 grid-columns 关键字值。然后,焦点顺序将变为 5、6、9、7、10、1、2、11、3、4、8、12。

.wrapper {
  reading-flow: grid-columns;
}

您也可以尝试使用 grid-order。焦点顺序保持为 1 到 12。这是因为没有为任何商品设置 CSS 订单。

使用 reading-order 的块容器

借助 reading-order 属性,您可以指定应在阅读流程中何时访问某个项,从而替换 reading-flow 属性设置的顺序。只有当 reading-flow 属性不为 normal 时,此属性才会对有效的阅读流程容器生效。

.wrapper {
  display: block;
  reading-flow: source-order;
}

.top {
  reading-order: -1;
  inset-inline-start: 50px;
  inset-block-start: 50px;
}

以下块容器包含五项内容。没有任何布局规则会按其来源顺序重新排列元素,但有一个非流式项应先访问。

<div class="wrapper">
  <a href="#">Item 1</a>
  <a href="#">Item 2</a>
  <a href="#">Item 3</a>
  <a href="#">Item 4</a>
  <a class="top" href="#">Item 5</a>
</div>

通过将此项的 reading-order 设置为 -1,焦点顺序会先访问它,然后再回退到阅读流程其余项的来源顺序。

您可以在 chrome.dev 网站上找到更多示例。

与 tabindex 交互

过去,开发者使用 HTML tabindex 全局属性使 HTML 元素可聚焦,并确定顺序焦点导航的相对顺序。不过,此属性存在许多缺点和无障碍问题。主要问题是,无障碍树无法识别使用正 tabindex 创建的按 tabindex 顺序的焦点导航。如果使用不当,焦点顺序可能会跳跃,与屏幕阅读器上的体验不一致。要解决此问题,请使用 aria-owns HTML 属性跟踪排序。

在前面的 flex 示例中,若要获得与使用 reading-flow: flex-visual 相同的结果,您可以执行以下操作。

<div class="box" aria-owns="one three two">
  <a href="#" tabindex="1" id="one">One</a>
  <a href="#" tabindex="3" id="two">Two</a>
  <a href="#" tabindex="2" id="three">Three</a>
</div>

但是,如果容器之外的其他元素也具有 tabindex=1,会怎么样? 然后,系统会一起访问具有 tabindex=1 的所有元素,然后再移至下一个增量 tabindex 值。这种跳跃的顺序导航会导致用户体验不佳。因此,无障碍功能专家建议避免使用正 tabindex。在设计 reading-flow 时,我们尝试解决了这个问题。

设置了 reading-flow 属性的容器会成为焦点范围所有者。这意味着,它会将顺序焦点导航范围限定为访问容器内的每个元素,然后再移动到网页文档中的下一个可聚焦元素。此外,系统会使用 reading-flow 属性对其直接子项进行排序,并会忽略正 tabindex 以进行排序。您仍然可以在阅读流内容项的后代上设置正 tabindex。

请注意,具有 display: contents 且从其布局父项继承 reading-flow 属性的元素也将是一个有效的阅读流容器。在设计网站时,请注意这一点。如需详细了解,请参阅我们就 reading-flowdisplay: contents 征求反馈的请求

请告诉我们

请试用本文中的示例以及 chrome.dev 上的 reading-flow 示例,并在您的网站上使用这些 CSS 属性。如果您有任何反馈,请在 CSS 工作组 GitHub 代码库中提交问题。如果您对 tabindex 和焦点范围行为有具体反馈,请在 HTML WHATNOT GitHub 代码库中提出问题。我们非常期待收到您对此功能的反馈。