生产环境禁用样式必须同时写 button:disabled 和 button[disabled],并显式设置 cursor、color、background-color 等属性以保障可访问性与兼容性。

button:disabled 和 button[disabled] 必须同时写
只写 button:disabled 会漏掉 SSR 渲染的按钮、React/Vue 中未透传属性的组件、或 JS 用 setAttribute('disabled', '') 动态添加的按钮;只写 button[disabled] 又会漏掉 JS 直接设 el.disabled = true 后触发的状态变化。两者行为不重合,必须并列声明:
-
button:disabled, button[disabled]是生产环境底线写法 - 扩展到其他控件:
input:disabled, input[disabled]、select:disabled, select[disabled] - 自定义组件加类名支持:
.my-button:disabled, .my-button[disabled]
禁用样式不能只靠 opacity 或 filter
opacity: 0.5 在深色模式或高对比度下文字可能不可读;filter: grayscale(1) 在 iOS Safari 旧版常渲染为全黑,且两者都不改光标、不阻焦点、不更新语义。真正可用的组合是显式控制多个通道:
- 必须加
cursor: not-allowed(浏览器不会自动加) - 显式设
color: #999、background-color: #f5f5f5、border-color: #ddd,确保 WCAG 对比度 ≥ 4.5:1 - 禁用过渡残留:
transition: none - 慎用
pointer-events: none——它会让 Tab 键跳过该按钮,破坏键盘导航
非原生按钮根本匹配不到 :disabled
:disabled 只对 <button>、<input>、<select>、<textarea> 生效。给 <div disabled> 或封装组件加 disabled 属性,CSS 完全无视。
- 优先换回语义化
<button type="button">,别用div模拟按钮 - 必须用自定义组件时,改用 class 控制:
<my-button class="is-disabled">,再配.is-disabled { cursor: not-allowed; color: #999; } - 同步加
aria-disabled="true"和tabindex="-1",否则屏幕阅读器和键盘用户感知不到禁用
fieldset[disabled] 下的子元素不触发 :disabled
当用 <fieldset disabled> 批量禁用一组控件时,内部的 <input>、<button> 不会匹配 input:disabled 或 button:disabled——这是 HTML 规范行为,不是 CSS bug。
立即学习“前端免费学习笔记(深入)”;
- 解决方案只有:
fieldset[disabled] input、fieldset[disabled] button - 避免混用
fieldset[disabled]和单个控件的disabled,否则样式优先级难控 - 测试时在 DevTools 里手动删掉
disabled属性,看样式是否立刻回退——这是验证伪类是否真在起作用的最快方式


















