必须组合使用 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500(深色模式加 dark:focus:ring-blue-400),并优先用 focus-visible: 替代 focus: 以保障可访问性。

focus:ring-2 单独用根本不管用
直接写 focus:ring-2 几乎看不到效果,或者出现双环、环贴边、深色模式下不可见——因为 ring 是用 box-shadow 模拟的,它不自动覆盖浏览器原生 outline,也不预留内边距空间。
必须显式关闭原生 outline:focus:outline-none,否则鼠标点击后会同时渲染 outline + ring,视觉混乱;ring-offset- 也不是可选项,它是“预留阴影内边距”,若父容器没设 ring-offset-color(默认透明),环就紧贴元素边缘,看起来像没生效。
- 基础组合必须包含:
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 - 深色模式下加
dark:focus:ring-blue-400,否则浅色环在暗背景上对比度不足(WCAG AA 要求 ≥ 4.5:1) - 按钮类元素建议加
rounded-lg,输入框类建议加focus:ring-inset避免撑高容器
focus-visible: 才是键盘导航的正确触发条件
用 focus: 会在鼠标点击后也触发焦点环,干扰体验;而 focus-visible: 只在键盘 Tab 导航或屏幕阅读器聚焦时生效,更符合可访问性规范。
不要混用 focus: 和 focus-visible:,否则鼠标点击后仍会闪一下环。旧版 Safari 不支持 focus-visible,必要时可在 tailwind.config.js 中配 fallback 插件,或降级为 focus:(仅限极简场景)。
- 推荐写法:
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2 - 非交互容器(如只读
<div>)别加 <code>tabindex,否则它能获得焦点却无意义 - 移动端真机测试时,务必连键入设备验证 Tab 流是否连续、环是否稳定出现
- 图标按钮若用了
scale-105,ring 也会被缩放——得配合transform: scale()和will-change: transform稳定渲染 - 弹出层(如
Popover)内使用时,若父容器有overflow-hidden,ring 会被截断,此时要给父容器加ring-offset-4扩大预留空间 - 批量渲染组件(如列表项、卡片)避免无差别加
focus-visible:ring,高频重绘在低端设备上可能导致切换卡顿 - 禁用
text-sm+text-gray-400这类组合 - 深色模式下,
ring颜色需重新验证对比度,不能简单套用浅色值 - 所有焦点环颜色必须通过自动化工具验证,而不是“看起来还行”
ring 在不同组件里表现不一致的根本原因
ring 渲染依赖 box-shadow,而实际落点受元素自身 border-radius、padding、甚至 transform 影响。同一个 ring-2 在 rounded-full 按钮和 rounded-none 输入框中,视觉粗细和圆润度完全不同。
颜色对比度不是凭感觉选的
text-blue-800 和 bg-white 看似对比高,实测可能只有 3.2:1(低于 WCAG AA 的 4.5:1)。小字号 + 浅灰文字(如 text-gray-400)是常见失败点。
应在 tailwind.config.js 的 theme.extend.colors 中,把 WebAIM Contrast Checker 验证过的高对比色单独定义为 accessibleBlue 等语义名,避免重复试错。











