:focus-visible需手动添加,因现代浏览器仅在键盘触发时生效,触控板或开关控制设备可能无法触发;gradio等框架常移除原生焦点能力,导致:focus-visible失效,故须显式设tabindex="0"并用:focus与:focus-visible双重声明。

focus-visible 为什么必须手动加,而不是靠浏览器默认?
现代浏览器对 :focus-visible 的支持是渐进式启用的,Chrome 86+ 默认只在“键盘触发焦点”时生效,鼠标点击后不显示焦点环——这看似合理,实则埋雷:屏幕阅读器用户常配合键盘导航,但若他们用触控板或开关控制设备(Switch Control),焦点行为可能被识别为“非键盘触发”,导致 :focus-visible 不生效,焦点彻底不可见。
更麻烦的是,Gradio、Streamlit 等低代码框架生成的组件(如 gr.Button)内部常把 tabindex="-1" 或移除原生焦点能力,再用 JS 模拟交互,此时连 :focus-visible 都无法捕获。
- 必须显式给所有可交互元素加
tabindex="0"(除非它是原生可聚焦标签如button、a、input) - 用
:focus+:focus-visible双重声明,避免旧浏览器降级失效:button:focus { outline: 2px solid #0066cc; }button:focus-visible { outline: 2px solid #0066cc; } - 禁用
outline: none后,务必补足等效视觉反馈;box-shadow 可用,但需确保在高对比度模式下仍可见(Windows HC 模式会强制覆盖部分 shadow)
ARIA label 和 aria-describedby 到底该用哪个?
两者不是互斥替代,而是解决不同问题:aria-label 是“我就是这个东西”,aria-describedby 是“我旁边有个说明,你得一起读”。很多开发者一上来就给按钮加 aria-label="Submit",结果屏幕阅读器读成“Submit Submit”,因为按钮内已有文本“提交”。
真实场景中,常见误用:
- Tooltip 类提示(如悬停显示“置信度:92%”)必须用
aria-describedby关联一个隐藏的<div id="desc-1">置信度:92%</div>,不能只靠title属性(Gradio 会剥离它) - 图标按钮(如
<button><svg>...</svg></button>)没有文字时,才用aria-label;若有文字(如“删除”),应删掉 SVG 的aria-hidden="false"并设aria-hidden="true",避免重复播报 - 表单字段错误提示,必须用
aria-invalid="true"+aria-describedby="error-1",且error-1对应的元素需在 DOM 中紧邻或逻辑上可关联
对比度检测为什么不能看设计稿色值?
设计稿里标 #666 文字配 #fff 背景,看着挺灰,但实测对比度只有 3.8:1 —— 直接不满足 WCAG 2.1 AA 级对小字号文本 ≥4.5:1 的硬性要求。原因在于:sRGB 色值不能直接套公式计算,必须先转线性光再代入 (L1 + 0.05) / (L2 + 0.05)。肉眼判断误差常达 2:1,尤其在 LCD 屏、阳光直射或轻度视力下降场景下完全失准。
真正可靠的路径只有一条:用工具实测。
- 开发阶段用 axe DevTools 浏览器插件,一键扫描当前页所有文本对比度违规
- CI 流程中集成
contrast-checker-cli,对关键色块做自动化断言 - SVG 图标别用固定色值描边,改用
currentcolor继承父级文字色,统一走文本对比度逻辑 - 开关(toggle)、滑块(slider)等非文本控件,前景与背景对比度也必须 ≥3:1;纯颜色标识(如绿色=开启)必须叠加图标或文字
Gradio/Streamlit 项目怎么不动大结构实现 AA 合规?
这类框架默认不输出语义化 HTML,也不管控焦点流,但改造不需要重写 UI。核心动作就三步:注入语义、接管焦点、补足描述。
- 在
gr.Interface初始化时传入elem_id="main-app",然后用 CSS 选择器精准覆盖:#main-app .gr-button { padding: 8px 16px; border-radius: 4px; } - 用
gr.Markdown插入带aria-live="polite"的隐藏区域,用于动态播报状态变更(如“检测完成,共识别出3个目标”) - 所有
gr.Slider、gr.CheckboxGroup等组件,手动加label标签并绑定for属性,Gradio 的info参数不生成可访问 label - 禁用 Gradio 自带的动画(
animate=True),因为自动滚动、淡入淡出会干扰屏幕阅读器节奏,且可能触发光敏癫痫风险(WCAG 2.1 2.3.1)
最易被忽略的是:Gradio 动态渲染的折叠面板(gr.Accordion)默认没加 aria-expanded 和 aria-controls,键盘用户根本不知道它能展开——必须用 JS 补全这些属性,并监听 click/focus 事件同步更新。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











