必须用 table 而非 role="grid" 做评分类单选网格,因其原生支持行列解析、跨版本兼容;role="grid" 需手动实现导航与通知,易出错且动态更新时 aria-labelledby 易失效。

table 是承载多维结构化数据网格的唯一推荐语义容器,role="grid" 仅适用于纯视觉对齐、无行列逻辑的数据流(如商品卡片墙),不能替代 table 处理评分表、问卷、课程表等真实二维关系。
为什么必须用 table 而不是 role="grid" 做评分类单选网格
屏幕阅读器对 table 的行列解析是原生、稳定、跨版本兼容的;而 role="grid" 需要手动实现方向键导航、焦点管理、动态更新通知,稍有遗漏就会导致视障用户卡在某个单元格无法移动或完全听不到上下文。
更关键的是:当某行问题缺失、某列选项动态增删时,table 的 DOM 结构天然保持行列对齐,role="grid" 容易因 JS 渲染错位导致 aria-labelledby 指向失效——比如 q3 实际渲染到了第4行,但 ID 还挂在第3行的 <th> 上。
aria-labelledby 必须同时引用行头和列头 ID
只关联 q2 或只关联 col-plus1,屏幕阅读器只会读“未选中单选按钮”或“+1”,丢失完整语义。每个 <input type="radio"> 必须显式写:aria-labelledby="q2 col-plus1"
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- ID 命名需稳定:避免用 React key 或随机字符串生成 ID,否则服务端渲染与客户端 hydrate 后 ID 不一致
- 空白列头必须补
aria-label:例如首列无文字的<th scope="col"></th>,应改为<th scope="col" aria-label="评分选项"></th> - 不要依赖
scope单独工作:scope="col"对理解有辅助,但不触发播报;aria-labelledby才是强制绑定播报内容的刚需
caption 不是可选项,而是无障碍第一道门槛
没有 caption 的表格,屏幕阅读器用户打开页面后无法快速判断“这是什么表”,只能逐行听读,成本极高。它必须作为 table 的第一个子元素,不能包在 thead 里,也不能靠 CSS 显示/隐藏来控制可见性。
- 文案要具业务含义:写“用户满意度评分表(2026年Q2)”比“评分表格”更有效
- 禁止使用已废弃的
summary属性:所有现代屏幕阅读器都不支持,且 Lighthouse 直接报错 - 若表格嵌套在组件中(如 Vue/React 封装的
<RatingGrid>),确保最终输出的 HTML 中caption仍紧贴table开始标签
table 缺了 caption 或某行 <th scope="row"> 没给 ID,屏幕阅读器在首次加载时就已经丢失上下文——后续所有 JS 绑定都只是在修一个已经塌掉的地基。


















