必须作用于html元素并用CSS变量控制灰度值,即:root{--grayscale:100%};html{filter:grayscale(var(--grayscale));-webkit-filter:grayscale(var(--grayscale));},JS仅动态修改--grayscale值即可实现灵活、可逆、全量覆盖的全局置灰。

直接给 html 元素加 filter 就能全局置灰,但硬编码写死会失去灵活性
很多项目一开始用 html { filter: grayscale(100%); -webkit-filter: grayscale(100%); } 一行搞定,看似省事,但后续要支持「手动开关」「深色模式下禁用灰度」「按日期自动启用」时就卡住了——所有逻辑都得靠 JS 操作 class 或内联样式,容易漏、难维护、无法响应式联动。
真正可扩展的做法是把灰度强度抽成 CSS 自定义属性,让 JS 只负责改变量值,样式层完全解耦:
-
:root里声明--grayscale: 100%;,初始值即全灰 -
html元素上统一用filter: grayscale(var(--grayscale)); - JS 只需执行
document.documentElement.style.setProperty('--grayscale', '0%')就能瞬间恢复彩色
--grayscale 值设为 0%、100% 还是数字?别用小数或无单位值
浏览器对 grayscale() 的参数类型很敏感:它只接受百分比(0%、100%)或无单位数字(0、1),但二者行为不一致。实测发现:
- 设为
0或1时,部分旧版 Safari(≤15.6)会忽略该值,导致灰度失效 - 设为
0%/100%兼容性最稳,Chrome、Firefox、Edge、Safari 全支持 - 不要用
0.5这类小数——grayscale(0.5)是非法语法,会被浏览器静默丢弃
所以始终用带 % 的写法:--grayscale: 100%;,切换时也用 '0%' 和 '100%' 字符串。
立即学习“前端免费学习笔记(深入)”;
为什么必须同时写 -webkit-filter?不是所有浏览器都认 filter
虽然现代浏览器基本都支持标准 filter,但 Safari 在 iOS 15.4 及更早版本中仍要求显式写 -webkit-filter 才能生效;而这个前缀在 Chrome/Firefox 中会被忽略,无副作用。所以安全写法是:
html {
filter: grayscale(var(--grayscale));
-webkit-filter: grayscale(var(--grayscale));
}
注意两点:
- 不能只写
-webkit-filter,否则 Firefox/Edge 会完全失效 - 不要重复声明多个 vendor 前缀(如
-moz-filter),它们早已被废弃且可能触发渲染 bug
灰度开关要「可逆」,就得避开 body 或局部容器,只作用于 html
有人习惯给 body 加灰度,结果发现导航栏固定定位后悬浮在灰度层之上,颜色突兀;或者弹窗、Tooltip 等绝对定位元素没被滤镜覆盖。根本原因是:filter 会创建新的 stacking context 和 containing block,影响子元素的层叠和尺寸计算。
正确做法是只对 html 元素应用灰度:
-
html是整个文档的根,它的filter会向下透传到所有后代(包括fixed、absolute元素) - 避免污染 DOM 结构,不用额外加 wrapper
div或 class 切换 - 如果页面用了
transform或will-change触发硬件加速,灰度滤镜可能被降级为 CPU 渲染——此时可加transform: translateZ(0)强制 GPU 合成
真正难的从来不是怎么变灰,而是怎么在不影响交互、不破坏定位、不拖慢性能的前提下,让灰度来得干净、走得彻底。


















