本站为个人非经营性网站,内容仅代表个人观点与记录,不构成任何专业建议。详见免责声明。

语义化 HTML 与无障碍:一次自查记录

Tab 键我按了四十多下,才把一篇文章页从头走到尾。

问题在前面九下。站名一下,页头七个导航链接七下,免责声明条里那个链接一下。每个页面的页头都一样,所以这九下要在每一页重复一次。

整站走一遍,记下几个改对的和几个改错的。

页面里第一个元素是「跳到主要内容」。它最早是这么写的:

.skip-link { display: none; }
.skip-link:focus { display: block; }

我当时觉得这个写法挺聪明:不用的时候它不存在,聚焦的时候它才出现。

实际上它一次都没出现过。display: none 的元素不在渲染树里,拿不到焦点——鼠标点不到,键盘也 Tab 不到,而 :focus 那条规则正等着它被聚焦才生效。这是个把自己锁死的写法。

正确的做法是让它留在渲染树里,只挪到屏幕外:

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus { left: 0; }

改完之后,按第一下 Tab 它就出来了,回车焦点落到正文上。前面那九下变成了一下。

这里有个容易漏的细节:跳转之后焦点得真的跟着走,否则只省下了一次滚动。我在 <main> 上加了 tabindex="-1",写成负一是为了不把它塞进 Tab 顺序。

第二个错:我拿标题的级别当字号用了

文章里 <h2> 是小节,<h3> 更细一层。文内目录那个脚本是这样找小节的:body.querySelectorAll('h2[id]')。某一节写成 <h3>,它就不会出现在目录里。

我在标签总页犯了这个错。整页只有一个 <h1>,下面的标签名我写成了 <h3>,理由很实际:<h2> 的默认字号在那页显得太大。这个理由和结构无关。

后果有两个。一个是按标题级别跳读时二级是空的,三级里却装着内容;另一个更隐蔽——哪天我为字号把一个二级标题写成三级,那一节会静默地从目录里消失,不报错,也不提示。

第三个:分隔符被算进了文本里

文章头部那行元信息是「技术随笔 · 两个标签 · 一个日期」,中间的点号是 <span class="sep">·</span>。

在辅助功能面板里,这一行拼出来的可读文本是「技术随笔 · 可访问性 · 前端 · 2026 年 9 月 12 日」,点号被当成内容。可它是排版的产物。加上 aria-hidden="true" 之后它就不在辅助功能树里了,页脚两个备案号中间那条竖线同理。

第四个:aria-current 与焦点轮廓

页头导航里有当前页。我最早的做法是给它加一个类名,只改颜色。颜色对读屏没意义。改成 aria-current="page" 之后,辅助功能树里会标出这是当前项,样式也跟着改成按属性选。

焦点轮廓我犯的错正好相反。早期写过 a:focus { outline: none },因为鼠标点链接时那个方框碍眼。现在用 :focus-visible:键盘操作时出现,鼠标点击时不出现。轮廓没删,两像素宽,强调色,往外偏移两像素。

没有真正听过的那一半

上面这些我全靠键盘和辅助功能面板查出来,没装过读屏软件,也没真的听过这个站被读出来是什么样子。

辅助功能树能告诉我一个元素有没有名字、角色对不对、顺序合不合理,但有几件事它显示不出来:一段话读出来的快慢,相邻元素会不会被连成一句,表格里两个单元格的对应。这些得靠耳朵,我一样都没验证。所以这篇只能叫自查记录。

还有一件我不确定:给 <main> 加 tabindex="-1" 之后,旧一些的浏览器会不会带出奇怪的焦点行为。

下一步我打算装一个读屏软件,戴上耳机把首页和这篇从头听一遍。如果听到一半就受不了,那说明前面几节还差得远。

本文为本人原创,首发于本站,采用 CC BY-NC-ND 4.0 许可,转载请注明作者「慎行」与原文链接。详见版权声明。

本文为个人学习与记录,不构成任何专业建议。