必须用 disabled 属性 + :disabled 伪类,因为只有原生表单控件响应 disabled,能自动处理焦点、tab 序、提交和屏幕阅读器;其他方式(如 div 加 disabled 或仅 css)无效,且需同时写 button:disabled 和 button[disabled] 确保 ssr 与动态 js 一致。

必须用 disabled 属性 + :disabled 伪类,仅靠 CSS 样式(如 pointer-events: none)无法满足可访问性要求。
为什么 :disabled 是唯一可靠起点
只有原生表单控件(button、input、select、textarea)真正响应 disabled 属性:它会自动移除键盘焦点能力、跳过 Tab 键序、阻止表单提交、被屏幕阅读器忽略。其他方式——比如给 div 加 disabled="true" 或只写 class="is-disabled"——浏览器根本不识别,:disabled 也完全不匹配。
常见错误包括:
- 用
<div role="button" disabled> —— <code>disabled对div是无效属性,CSS 和语义都失效 - React 中写
disabled={isSubmitting ? "disabled" : false}—— 字符串值不会触发:disabled,应传布尔值disabled={isSubmitting} - Vue 中写
disabled="isSubmitting"(未用v-bind)—— 属性值是字符串字面量,不是响应式布尔值
:disabled 和 [disabled] 必须同时写
现代框架(React/Vue)动态设置 disabled 时,通常只更新 DOM property(el.disabled = true),不写入 HTML attribute;而服务端渲染(SSR)输出的是静态 disabled 属性,JS 尚未执行。两者行为不一致,单靠任一选择器都会漏掉样式。
正确写法是并列声明:
button:disabled, button[disabled] {
background-color: #e0e0e0;
color: #666;
cursor: not-allowed;
opacity: 0.6;
}
注意:button[disabled] 权重仅 10,容易被 .btn-primary:hover(权重 20)覆盖,导致禁用后仍显示悬停色——务必检查开发者工具中该规则是否被划掉。
禁用态视觉反馈不能只靠灰度
单纯设 opacity: 0.5 或 color: #999 对色觉障碍用户不可靠,WCAG 要求禁用文本与背景对比度 ≥ 4.5:1,且不能仅依赖颜色区分状态。
推荐组合控制:
- 显式重置交互反馈:
cursor: not-allowed、移除box-shadow、禁用过渡:transition: none - 避免纯灰背景:
background-color: #e0e0e0比#cccccc更稳妥,文字用#666而非#999保对比度 - 禁用所有伪类干扰:
button:disabled:hover、button:disabled:focus都要显式重写或清空,防止继承启用态样式
非原生按钮(如 div 模拟)必须手动补全语义
如果真没法换回 button(比如设计限制或遗留代码),必须同步做三件事:
- 加
tabindex="-1"阻止键盘聚焦 - 加
aria-disabled="true"告知屏幕阅读器 - 用 class 控制样式,例如
.btn--disabled,并确保该 class 在 JS 中准确切换
但要注意:pointer-events: none 本身不阻止键盘操作,也不能替代 disabled 属性——它只是辅助屏蔽鼠标,且在 IE10 及更早版本中完全不支持。
最易被忽略的点:禁用状态的核心不是“看起来灰”,而是“行为断开 + 语义明确”。哪怕样式再精致,只要没设原生 disabled 属性,就等于没禁用。











