题干与适用场景
设计系统要统一按钮、输入框、菜单和自定义控件的键盘焦点样式。现有实现只改变边框颜色,低对比度主题下几乎不可见, 并在组件获得焦点时改变尺寸导致布局抖动。请根据 WCAG 2.2 的 Focus Appearance 设计可复用方案,并给出可自动化和人工验证的门禁。
WCAG 2.2 将 Focus Appearance 作为新的成功标准,要求焦点指示器具备足够的面积和与相邻颜色的对比度。题目考察候选人能否把 规范转成组件状态、CSS 绘制、颜色令牌、键盘路径和测试,而不是只说“加 outline”。
面试官考察点
重点包括区分焦点指示器与组件本身、计算面积与对比度、避免只用颜色、处理圆角和非矩形控件、避免布局位移、保留原生可访问语义, 以及覆盖鼠标、键盘、触摸、系统高对比度和 :focus-visible 的行为。
回答前需要澄清的问题
- 目标是 WCAG 2.2 AA 还是 AAA,哪些路径属于键盘可操作?
- 焦点环是组件外部、内部,还是使用
outline-offset? - 设计系统是否有主题令牌和高对比度模式?
- 自定义控件是否使用原生按钮、输入框和正确的 ARIA role?
- 组件在 focus、hover、selected、error 状态叠加时如何保持可辨识?
- 如何测试非矩形、滚动容器和暗色背景?
30 秒回答框架
“先确认标准级别和可操作元素,再为所有可聚焦状态提供不依赖颜色的焦点环。使用不占布局空间的 outline 或 inset/外部 box-shadow, 用设计令牌保证与组件和相邻背景的对比,并在高对比度模式保留系统可见性。用键盘路径、截图像素采样、浏览器可访问性树和人工放大检查验证。”
分步骤深入解答
第一步:明确焦点对象和状态模型
每个可操作控件必须有真实焦点目标,优先使用原生按钮、输入框、选择框和链接;自定义 div 只有在无法使用原生元素时才增加 键盘处理、role、name、value 和状态同步。focus-visible 用于区分键盘焦点与指针点击,但不能让键盘焦点消失。
定义 default、hover、focus-visible、selected、invalid、disabled 状态优先级,确保 focus 环在错误或选中状态下仍存在。
第二步:选择不改变布局的绘制方式
避免通过增加 border 宽度制造焦点,因为它会改变盒尺寸并推动邻居。优先使用 outline、outline-offset 或伪元素;复杂形状可用 inset 与外部 阴影组合,但要验证不会被 overflow 截断。
.control:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 2px;
}焦点环的视觉面积要围绕实际组件边界评估;圆角、图标按钮和自定义 slider 不能只在矩形外框画一个被裁掉的像素线。
第三步:把 WCAG 2.4.13 转成令牌和阈值
为环宽、偏移、焦点颜色、背景颜色和错误状态建立令牌,禁止组件各自选颜色。检查焦点指示器与未聚焦组件颜色的对比,以及与紧邻背景的对比;不要只比较文字颜色。
focus-ring-width >= 2 CSS px
focus-ring-contrast-against-adjacent >= required threshold
focus-ring-area >= minimum perimeter-area rule规范中的面积和对比计算要按实际渲染像素与组件几何验证,不能用设计稿颜色值代替。遇到渐变、图片背景或半透明层,使用最不利背景采样。
第四步:处理主题、高对比度和系统覆盖
暗色主题、浅色主题和品牌主题都要有独立焦点令牌。支持 forced-colors: active 时不要强制覆盖系统焦点颜色;可使用 outline: 2px solid ButtonText 等系统颜色让平台提供可见对比。
@media (forced-colors: active) {
.control:focus-visible {
outline: 2px solid ButtonText;
outline-offset: 2px;
}
}不要用 outline: none 清除浏览器焦点,除非同一规则在同一状态提供等价或更清晰的可见指示器。
第五步:覆盖动态组件与滚动场景
菜单、对话框、组合框和虚拟列表要验证焦点移动、关闭后回到触发元素、滚动不遮挡焦点环。焦点环不能只存在于不可见的原始节点, 也不能因为 transform、clip-path 或 overflow 把它裁掉。
自定义控件的 aria-activedescendant 场景要显示当前活动项;如果焦点留在容器上,视觉指示器必须跟随活动项,并在屏幕阅读器和键盘路径中一致。
第六步:建立自动化和人工验收
自动化检查 DOM 的可聚焦元素、focus-visible 样式、计算后的 outline/box-shadow、主题令牌和 forced-colors 规则;截图测试覆盖组件状态和背景切片。 人工测试用键盘 Tab、Shift+Tab、Enter、Space、箭头键和 Escape,放大 200% 检查裁剪与布局,使用系统高对比度和不同输入设备复验。
验收记录组件、状态、背景、环几何、对比结果、键盘步骤和缺陷截图。只在设计稿上看见一条蓝线不能证明符合标准。
高质量示范回答
“我先盘点所有可操作元素,优先原生控件;为 :focus-visible 使用不占布局的 outline 和偏移,令牌统一环宽、颜色和背景对比。焦点环不能只靠颜色, 也不能在 focus 时增加 border 推动布局。暗色、浅色和 forced-colors 分开验收,并保留系统可见性。”
“对菜单、对话框、虚拟列表和非矩形控件,我会验证焦点移动、回焦、裁剪和活动项指示。自动化采集计算样式与截图,人工用完整键盘路径、200% 缩放和高对比度模式复验。”
常见错误
- 只改变文字或边框颜色 → 焦点环可能与背景融为一体 → 按环与相邻颜色分别验证。
- 增加 border 宽度 → 页面布局跳动 → 使用 outline 或不占布局伪元素。
- 强制
outline: none→ 键盘用户失去反馈 → 同一状态提供等价可见指示器。 - 只在浅色主题测试 → 暗色或强制颜色模式不可见 → 覆盖主题和 forced-colors。
- 只用矩形截图 → 圆角、裁剪和虚拟列表漏测 → 测试实际几何和滚动路径。
- 把焦点放到 div → 语义、键盘和读屏不一致 → 优先原生控件并维护 ARIA 状态。
追问及应对
追问一:outline 和 box-shadow 哪个更好?
没有普遍唯一答案。outline 不占布局且有系统语义,box-shadow 可绘制多层或内外环;按裁剪、圆角、forced-colors 和实际像素验证选择。
追问二:为什么 :focus-visible 不能替代 :focus?
它是浏览器判断何时显示焦点的启发式,不能让程序失去焦点语义。对键盘和辅助技术路径必须保留清晰指示器,并测试浏览器差异。
追问三:组件已有 error 红框,焦点环还需要吗?
需要。错误状态说明内容问题,焦点环说明当前操作目标;两者应同时可见且不只依赖颜色。
追问四:背景是图片或渐变如何测对比?
在焦点环实际像素覆盖的相邻区域采样最不利颜色,必要时增加不透明底层或双层环;设计稿平均颜色不足以证明合规。
追问五:如何避免 focus ring 被 overflow 截断?
检查滚动容器和祖先的 overflow、clip-path 与 transform;用内环、额外 padding 或组件层伪元素调整几何,不能简单关闭焦点样式。