Front-End-Checklist 无障碍清单:正确使用列表结构(ul/ol/li)——从 HTML 语义规则到源码级校验实践
【免费下载链接】Front-End-Checklist🗂 The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist
本篇文章以开源仓库 Front-End-Checklist 的 list-structure 技能文档 及其参考实现 为主体,系统讲解 Web 开发中最容易被忽视、却直接决定屏幕阅读器信息传达质量的列表结构规则。读完你将掌握:
<ul>/<ol>与<li>的正确父子关系约束、嵌套列表的规范写法、相关无障碍规则的配套关系,以及如何借助仓库内置的 MCP 代码审查工具实现自动化的列表结构检测。
列表是网页中最常见的语义结构之一,但恰恰因为常见,许多开发者会在<ul>中直接放入<div>、<p>甚至裸文本。屏幕阅读器依赖特定的父子关系来播报列表项数量与用户当前所在位置——一旦结构被破坏,"List of 3 items" 会变成乱报、漏报甚至完全失声。Front-End-Checklist 将"正确使用列表结构"(Use correct list structure)列为accessibility/document-structure分类下优先级为 medium、难度为 intermediate 的核心规则,并在 MCP 工具中提供了对应启发式检测实现。
规则一句话定义
Lists (
ul,ol) should only contain list item elements (li) to ensure they are correctly interpreted by assistive technology.
HTML 列表元素<ul>和<ol>有一个明确要求:它们的唯一直接子元素必须是<li>(以及规范允许的<script>与<template>元素)。这一点由 HTML Living Standard 的ul/ol元素定义明确写出,因此在列表内直接放置<div>等其他标签会破坏列表语义。原文出处见 list-structure 规则 MDX 文件,仓库同时将该规则收录进 rules-catalog 生成目录 与 README 清单。
快速参考:三条必须遵守的硬性约束
技能文档的 Quick Reference 部分给出了可直接照抄的三条规则:
- 无序列表
<ul>和有序列表<ol>中只能包含<li>元素; - 不要把
<div>、<p>等其他元素直接放进列表内部; - 嵌套列表也必须包在
<li>元素内部,而不能直接嵌在<ul>/<ol>的下一层。
这三条约束可以浓缩为一句话:检查所有列表的 DOM 结构,确保<ul>或<ol>的直接子元素只有<li>。
为什么列表结构如此重要
参考文档 references/rule.md 从四个维度解释了破坏列表结构的后果:
- 辅助技术解读:屏幕阅读器进入列表时会播报 "List of 3 items"(列表,共 3 项)。结构不正确会导致列表被数错项数,甚至被完全忽略。辅助技术依赖列表容器的存在来播报列表类型("bullet list" 无序列表 vs "numbered list" 有序列表)和总项数,失去容器后这些上下文信息会全部丢失。
- 标记校验:破坏列表的父子关系会使 HTML 无效,进而引发不可预测的渲染问题,XML 类工具在解析时也可能直接失败。
- 一致性:用户期望标准的列表行为(项目符号、编号),这些通过标准标记才能最可靠地实现。
- 搜索引擎解析:搜索引擎会利用列表结构理解内容之间的关系,结构损坏也会削弱这层信号。
规则 MDX 的whyItMatters字段做了更凝练的总结:屏幕阅读器依赖列表特定的父子关系,才能正确播报项数与当前位置。
代码示例:错误与正确写法对照
参考文档给出了完整的正反例,这是审核与修复时的直接依据:
<!-- 错误:列表的直接子元素是一个 div --> <ul> <div> <li>Item 1</li> <li>Item 2</li> </div> </ul> <!-- 正确:所有直接子元素都是 li --> <ul> <li>Item 1</li> <li>Item 2</li> <li> <!-- 嵌套列表必须放在 li 内部 --> <ul> <li>Sub-item 1</li> </ul> </li> </ul>注意第二个示例中的第三个<li>:嵌套的<ul>出现在<li>内部,而不是直接作为外层<ul>的子节点。这是 HTML 规范对嵌套列表的硬性要求——内层列表在语义上属于外层列表项的"内容",因此必须包裹在<li>中。
修复思路:从"移除"到"语义重构"
SKILL.md 的 Check/Fix/Explain 段落给出了完整的实操流程:
- Check(检查):检查所有列表的结构,确保
<ul>或<ol>的直接子元素只有<li>。 - Fix(修复):将
<ul>与<ol>内部的非<li>元素移除或移到列表之外。 - Explain(解释):向团队解释为什么辅助技术需要一致的列表结构来提供准确的导航信息。
对照仓库中 listitem 规则,当<li>被用于纯样式目的(比如只想画一个项目符号时),正确的修法不是硬塞一个列表容器,而是换成<div>或<p>并用 CSS 实现视觉效果;如果<li>出现在导航中,则要保持<nav><ul><li>的完整嵌套链。
易混淆的姊妹规则:一起审才完整
在 Front-End-Checklist 的规则体系中,list-structure属于accessibility/document-structure子分类,与该分类下的另外三条规则"成组出现、常被同时审查",关系定义在规则 MDX 的relatedRules字段中:
| 规则 slug | 侧重点 | 与 list-structure 的关系 |
|---|---|---|
| listitem | 每个<li>必须有<ul>/<ol>/<menu>作为直接父元素 | 互补:list-structure 管"列表容器内只能有 li",listitem 管"li 不能脱离列表容器" |
| definition-list | <dl>术语-定义列表的正确结构 | 同属 document-structure,一起审查 |
| dlitem | <dt>/<dd>必须位于<dl>内 | 同属 document-structure,一起审查 |
| semantic-lists | 相关条目组应使用ul/ol/dl而非样式化 div | 正向规则:不仅结构正确,还要"该用列表时用列表" |
其中 semantic-lists 提供了极好的补充视角:许多团队把"列表样式"用<div class="feature-list">实现,这虽然没有破坏任何父子关系,却让屏幕阅读器完全丧失列表上下文。正确做法是使用语义列表元素——<ul>用于无序项目(导航、特性、项目符号),<ol>用于有序序列(步骤、排名、指令),<dl>用于术语-定义对(术语表、FAQ)。
另外,listitem 规则 还提醒了一个真实场景中的坑:当<ul>上应用list-style: none时,部分浏览器(如 Safari/VoiceOver 组合)会剥离列表语义;若这是有意的(例如导航菜单),应在<ul>上补充role="list"以恢复列表语义。这也是 ARIA 体系中role="listitem"必须被role="list"拥有的要求在listitem规则里的体现。
例外情况与优先级判断
规则并非无脑一刀切,参考文档的 Exceptions 部分给出了三个重要的判断准则:
- 先处理最强语义问题:简单的数据表格,缺失表头关系带来的问题往往比缺少增强(如 caption 或移动端包装)更严重,应优先修复最强的语义缺陷;
- 不要为满足规则而伪造语义:不要把纯布局结构硬转成数据表格标记,正确的修法可能是彻底移除表格语义;
- 重叠问题分层解决:多个表格无障碍问题同时存在时,先解决表头单元格关系,因为下游的播报都依赖它。
这三条准则的内核是:规则服务于"感知、操作、理解",评估时应基于渲染后的真实体验而非仅仅静态源码。
标准依据
规则的来源与权威依据(见规则 MDX 的sources字段):
- HTML Living Standard:
ul元素与ol元素(primary 权威标准),明确规定了"唯一直接子元素为li/script/template"; - MDN:
<ul>元素参考; - 配套标准还包括W3C WAI WCAG Overview与MDN Accessibility(参考文档 Standards 部分),并且都强调:要对齐实现并验证渲染后的体验,而不仅仅是源码。
验证方法:自动化 + 手动双通道
参考文档的 Verification 部分给出完整的验证路径:
自动化检查
- 在浏览器无障碍树(accessibility tree)或无障碍面板中检查相关元素、角色与可访问名称;
- 在适用时运行自动化无障碍检查器,如axe或Lighthouse。axe DevTools 也列在规则 MDX 的
resources字段中作为推荐工具。
手动检查
- 使用纯键盘导航测试受影响的 UI,确认规则在渲染后的体验中成立;
- 如果该规则影响关键交互,用屏幕阅读器复测一条有代表性的用户流程。
源码级实践:MCP 工具如何自动检测列表结构
Front-End-Checklist 仓库的 MCP 服务将这条规则落地成了可执行的启发式检测。在 review-code.ts 中可以看到两个方向互补的检测逻辑(对应源码第 2269–2296 行):
检测方向一:<li>的直接父元素必须是合法列表容器
// ── list-structure/listitem: <li> must be direct child of <ul> or <ol> for (const li of root.querySelectorAll('li')) { const parent = li.parentNode const parentTag = parent?.rawTagName?.toLowerCase() if (parentTag && parentTag !== 'ul' && parentTag !== 'ol' && parentTag !== 'menu') { issues.set('listitem', '<li> element found outside <ul>, <ol>, or <menu> — list items must be direct children of a list container') break } }这段代码遍历所有<li>,一旦发现其直接父元素不是ul/ol/menu三者之一,就触发listitem问题——这正是前文 listitem 规则的自动化落地。
检测方向二:<ul>/<ol>的直接子元素必须是<li>
// ── list-structure: <ul>/<ol> direct children should be <li> (not bare text or other elements) for (const list of root.querySelectorAll('ul, ol')) { const badChildren = list.childNodes.filter(n => { if (n.nodeType === 3 /* TEXT_NODE */) return (n.rawText ?? '').trim().length > 0 const tag = (n as typeof list).rawTagName?.toLowerCase() return tag && tag !== 'li' && tag !== 'script' && tag !== 'template' }) if (badChildren.length > 0) { issues.set('list-structure', 'List element contains direct children that are not <li> — only <li> elements should be direct children of <ul>/<ol>') break } }注意这段实现与 HTML 规范的完全对齐:它把<script>与<template>排除在"非法直接子元素"之外(规范允许),同时把非空裸文本也判定为违规——这意味着在<ul>中直接书写文字同样会被标记。这是"既管标签、也管文本"的完整结构校验,也是我们在人工审核时容易遗漏的细节。
常见违规模式与修复清单
综合规则文档与源码检测逻辑,实战中最常遇到的违规模式可以归纳为以下四类:
| 违规模式 | 示例 | 正确修法 |
|---|---|---|
列表容器内直接放<div> | <ul><div><li>…</li></div></ul> | 移除<div>,让<li>直接成为<ul>子节点 |
列表容器内直接放<p>或裸文本 | <ul><p>说明</p><li>…</li></ul> | 将说明文字移到列表外部 |
<li>脱离列表容器 | <div><li>Contact us</li></div> | 用<ul>/<ol>包裹,或按样式需求换成<div>/<p> |
嵌套列表未包在<li>内 | <ul><li>A</li><ul><li>B</li></ul></ul> | 将内层<ul>放入外层某个<li>内部 |
模板化组件(如 React 的children透传、CMS 富文本渲染、样式重置中的裸<li>)是最容易产生上述违规的温床,审查渲染后的 DOM 时请优先扫描这几类来源。
实战落地建议
- 开发阶段:把 list-structure 与 listitem 两条规则纳入代码评审 checklist,配合 MCP 代码审查工具在提交前完成启发式扫描;
- 设计系统层面:封装统一的
List/ListItem组件(如 semantic-lists 规则提供的 React 示例 所示),从源头杜绝手写不规范结构;若使用list-style: none移除符号样式,记得在导航场景补role="list"; - 验证闭环:每次修复后走一遍"浏览器无障碍面板 → axe/Lighthouse → 屏幕阅读器复测"的流程,确认渲染后体验而非只盯源码;
- 优先级:当多个无障碍问题并存时,先处理对感知、操作、理解阻塞最严重的语义缺陷,不为了"凑规则"而伪造语义。
列表结构是"小规则、大影响"的典型:修复成本往往只需删除一个<div>或移动一个标签,却能直接决定视障用户能否准确获知"这个列表有几项、我在第几项"。把它纳入自动化检查与人工验证双通道,是构建真正无障碍前端的第一步。
【免费下载链接】Front-End-Checklist🗂 The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考