.sr-only 类需手动定义,其作用是视觉隐藏但保留屏幕阅读器可读性;直接使用 hidden 或 invisible 会使内容完全脱离可访问性树,导致语义缺失。

.sr-only 类不是 Tailwind 内置类,必须手动定义;直接用 hidden、invisible 或 aria-hidden="true" 都会让屏幕阅读器完全跳过内容——这在需要补充上下文(如“编辑按钮”旁的“用户资料”)时会直接导致可访问性失败。
为什么不能直接用 Tailwind 的 hidden 或 invisible
hidden 编译为 display: none,元素彻底脱离渲染树和可访问性树;invisible 是 visibility: hidden,虽保留布局占位,但同样从可访问性树中移除。两者都让屏幕阅读器「看不见也读不到」。而 .sr-only 的目标是「视觉不可见,但可读、可聚焦、可交互」。
常见翻车点:
- 把
.sr-only套在button内部文字上,却忘了给按钮本身加aria-label——结果屏幕阅读器只读按钮角色,不读隐藏文本,语义断裂 - 父容器设了
overflow: hidden,而.sr-only元素用了position: absolute向右偏移,被裁掉后读屏器就读不到 - 复制网上的
.sr-only代码但漏了border: 0,某些 NVDA + Firefox 组合会误报「有边框」干扰朗读
如何正确定义并注入 .sr-only 到 Tailwind 项目
Tailwind 不提供该类,需在 tailwind.config.js 的 theme.extend.fontSize 之外的 content 或自定义 CSS 中注入。推荐方式是在 src/index.css(或全局样式入口)里写:
立即学习“前端免费学习笔记(深入)”;
`.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}`
注意:clip 已被废弃,但仍是目前最兼容的写法(clip-path 在旧版 Safari 和部分读屏器中支持不佳);width 和 height 必须非零,否则某些读屏器(如 JAWS)会跳过该元素。
使用场景示例:
- 表格行操作列中,用
<span class="sr-only">用户 ID </span>12345,让读屏器读出「用户 ID 12345」而非仅「12345」 - 图标按钮内:
<button><svg>...</svg><span class="sr-only">删除此条记录</span></button>,比仅靠aria-label更利于翻译与动态内容更新 - 避免和
aria-label混用:如果按钮已有aria-label="编辑用户",再套.sr-only就冗余,甚至引发重复朗读
React/Vue 中动态添加 .sr-only 的坑
用 useEffect 或 v-if 控制类名开关,容易造成首次渲染闪现(sighted 用户看到文字一闪而过)或漏读(JS 执行晚于读屏器初始遍历)。
更稳妥的做法:
- 服务端渲染(SSR)或静态生成(SSG)时就带好
.sr-only类,确保首帧即正确 - 若必须动态控制,用
setTimeout(() => el.classList.add('sr-only'), 0)延迟一帧,避开 React/Vue 的 hydration 时机冲突 - 别用
v-show或ngIf切换显示状态——它们底层仍是display: none,破坏可访问性
真正难的不是写对那几行 CSS,而是判断「这段文字到底该由 .sr-only 补充,还是该由 aria-label/aria-labelledby 替换,抑或根本该用可见文案」——这个决策点,没有工具能替你做。



















