订单概览
查看今日订单状态。
我在整理响应式卡片网格时,最容易忽略的不是列数变化,而是键盘用户下一次按 Tab 会落到哪里:卡片视觉上已经换了位置,DOM 顺序却没有变,焦点就可能从右上角跳回左下角。解决这类问题的关键不是给每个卡片硬写一组 tabindex 数字,而是先保住合理的 DOM 顺序,再让网格容器用 reading-flow 表达阅读方向。
本文只讨论 CSS Grid 中可聚焦卡片的访问顺序。常用结论是:按视觉行访问用 grid-rows,按视觉列访问用 grid-columns;只有确实依赖子项 order 时才考虑 grid-order。reading-flow 与 reading-order 当前仍属于有限支持能力,生产环境应保留自然 DOM 顺序和可用的降级样式。
我处理这类布局时会先把卡片写成一个无需 CSS 也能顺读的列表。每张卡片如果本身不是链接或按钮,再用 tabindex="0" 让它进入正常焦点路径;不要一开始就使用正数 tabindex,也不要用大量 order 去掩盖内容顺序问题。
订单概览
查看今日订单状态。
库存提醒
查看需要补货的商品。
配送进度
查看正在配送的订单。
这里的中文注释放在文章解释中,而不是塞进 HTML 属性:卡片顺序由源代码先定义,CSS 只负责布局。如果浏览器不认识新的阅读顺序属性,用户仍然能看到内容并按 DOM 顺序访问。
最常见的卡片墙是从左到右、从上到下阅读。此时容器可以设置 display: grid,再用 reading-flow: grid-rows 表达“按视觉行访问”。MDN 对它的定义是:网格项目按视觉顺序逐行进入阅读顺序,同时考虑书写模式。
.card-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1rem;
/* 让阅读方向与网格的视觉行一致,默认仍可回退到 DOM 顺序 */
reading-flow: grid-rows;
}
.card {
min-block-size: 9rem;
padding: 1rem;
border: 1px solid #cbd5e1;
border-radius: 0.75rem;
/* 让自定义卡片进入正常 Tab 路径,并保持清晰焦点样式 */
outline: none;
}
.card:focus-visible {
box-shadow: 0 0 0 3px #93c5fd;
}

这段 CSS 的重点不在于把布局“排成某个固定顺序”,而在于把视觉布局和阅读顺序的职责分开:grid-template-columns 决定网格几何形状,reading-flow 负责可读和可聚焦内容的顺序。卡片数量增加或列数随媒体查询变化时,读者不需要重新维护一组焦点数字。
有些数据面板更像时间轴或对比矩阵,读者希望先读完第一列,再读第二列。此时把容器改成 reading-flow: grid-columns。它只改变阅读和导航顺序,不会改变卡片在页面上的位置。
.comparison-grid {
display: grid;
grid-template-columns: repeat(4, minmax(10rem, 1fr));
gap: 0.75rem;
/* 适合需要纵向逐列阅读的对比卡片 */
reading-flow: grid-columns;
}
@media (max-width: 48rem) {
.comparison-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
/* 小屏仍由同一阅读策略管理,避免另写 tabindex 序列 */
reading-flow: grid-columns;
}
}
不要把 grid-rows 和 grid-columns 理解成“修复所有顺序问题”的开关。它们表达的是容器级的视觉行列规则;如果内容本身有明确叙事顺序,最好直接调整 HTML 的源代码排列,让默认的 normal 就能成立。
grid-order 的边界更窄:它会在网格子项使用 order 时,把修改后的项目顺序纳入阅读顺序;如果子项没有设置 order,它的效果接近默认顺序。也就是说,不能为了追求“看起来和设计稿一样”就给所有项目加 order,然后再用 grid-order 收拾焦点。
.priority-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
/* 仅在业务确实使用 order 改变网格项目顺序时启用 */
reading-flow: grid-order;
}
.priority-grid .highlight {
order: -1;
}
/* 让不支持 reading-flow 的浏览器仍按合理 DOM 顺序呈现 */
@supports not (reading-flow: grid-order) {
.priority-grid {
reading-flow: normal;
}
.priority-grid .highlight {
order: initial;
}
}
如果视觉上必须把重点卡片提前,但内容阅读上不应该提前,优先考虑重新组织布局区域或复制清晰的标题层级,而不是强行改变焦点路径。布局修复不应牺牲用户对内容顺序的预期。
天然可聚焦的 a、button 等元素不需要额外设置 tabindex。自定义卡片若必须整体获得焦点,使用 tabindex="0",让它进入正常的顺序。MDN 说明,在 reading flow 容器中,tabindex="0" 的子项会按 reading-flow 和 reading-order 参与阅读与导航。
reading-order 适合做少量的分组微调,例如把一个“返回顶部”卡片放在同组项目之前或之后:
.card-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
reading-flow: grid-rows;
}
.card--summary {
/* 只改变阅读组位置,不改变卡片的视觉位置 */
reading-order: -1;
}
.card--footer {
/* 数值越大,越靠后进入阅读顺序 */
reading-order: 1;
}

不要使用 tabindex="1"、tabindex="2" 这样的正数来编排卡片。官方资料明确指出,reading flow 中正 tabindex 的排序意图会被覆盖,而且正 tabindex 本身也会让后续页面维护变得脆弱。更稳定的组合是:合理 DOM 顺序、自然焦点元素或 tabindex="0",再用容器级阅读属性表达确实存在的视觉变化。
这类属性目前不是所有主流浏览器都完整支持,MDN 将 reading-flow 和 reading-order 标为有限支持与实验性能力。生产上线前,我会把检查分成“支持时是否更好”和“不支持时是否还能用”两组:
grid-rows、grid-columns 或 grid-order 后,记录预期的行列顺序,不要只凭视觉猜测。官方资料可从这里复制查看:https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/reading-flow。它列出了 normal、grid-rows、grid-columns、grid-order 等值,以及与 tabindex 的交互边界。
不会。它改变的是阅读和顺序导航所使用的逻辑顺序,网格位置仍由 Grid 属性、区域和子项样式决定。
只有当卡片本身承担可操作任务时才适合。若卡片只是展示内容,应让内部的链接或按钮获得焦点,避免制造过多没有操作意义的停靠点。
不能。它适合少量、明确的阅读组调整;内容主顺序仍应通过 DOM 结构表达,并为不支持新属性的环境保留可用降级。
正数会把焦点顺序变成一份需要人工维护的全局清单,响应式列数和插入新卡片后很容易失效。优先使用自然焦点顺序、tabindex=0 和容器级阅读规则。
归纳起来,网格卡片先有正确的 DOM 顺序,再用 grid-rows 或 grid-columns 对齐视觉行列;grid-order 只服务于确实使用 order 的布局。把新属性当作渐进增强,并始终保留可用的键盘焦点样式和无障碍降级路径,才是这类布局调整的安全边界。
JavaScript Temporal 怎样处理跨时区日期计算