深色模式与高对比度模式必须分开处理:前者用 prefers-color-scheme 配合自定义颜色,后者需 prefers-contrast: high 和 forced-colors: active 双重覆盖,并强制使用系统颜色关键字、禁用圆角阴影、重置表单控件样式及添加语义标记。

prefers-color-scheme 和 prefers-contrast 必须分开处理
很多人以为开了 @media (prefers-color-scheme: dark) 就自动兼容高对比度,其实完全不是一回事。前者是用户主动选择的视觉偏好,后者是操作系统强制接管渲染策略——浏览器会直接重绘所有元素,连 background: linear-gradient 都会被抹成纯色,rgba(0,0,0,0.1) 会变成全黑或全白。
关键区别在于:深色模式靠 CSS 变量和媒体查询就能兜底;高对比度模式必须用 @media (prefers-contrast: high) + @media (forced-colors: active) 双重覆盖,且不能依赖任何自定义色值。
-
@media (prefers-color-scheme: dark)里可以放心用#1e1e1e、#e0e0e0这类具体颜色 -
@media (prefers-contrast: high)中所有颜色必须重置为系统关键字:Canvas、CanvasText、ButtonText、ButtonFace等 -
@media (forced-colors: active)要禁用所有box-shadow、border-radius、filter,否则会被系统忽略或导致元素“消失”
表单控件在高对比下失效的典型原因
复选框没勾、下拉箭头不见、密码框光标不可见——这些不是 bug,是样式没重置到位。系统高对比会强制覆盖原生控件的绘制逻辑,但不会帮你补语义或修复隐藏状态。
-
input[type="checkbox"]和input[type="radio"]必须设appearance: none,再用background-color: ButtonText+border: 2px solid ButtonFace手动画出可识别形状 -
select的默认下拉图标在高对比下常被清空,得用background-image: url("data:image/svg+xml,...")替换为高对比 SVG(推荐 Unicode 符号 ▼ 或 ▶) -
label和input必须用for/id显式绑定,不能靠 DOM 包裹——否则高对比下 label 文字可能被压暗,而 input 仍不可见 - 所有
placeholder要单独写::placeholder { color: GrayText; },系统不继承父级 color 规则
边框、焦点轮廓、文本包裹是高对比刚需
纯色块在高对比下极易“融化”进背景,尤其是按钮、输入框、卡片这类无文字锚点的元素。系统不会自动加边框,得你手动补。
- 所有交互元素至少加
1px solid CanvasText或1px solid ButtonText,避免视觉丢失 -
:focus-visible轮廓建议设为3px solid Highlight,粗细+颜色双保险,比outline: auto可靠得多 - 纯图标按钮(如
×或 SVG)必须加aria-label,且周围留白足够,否则高对比下图标可能缩成一个点 - 文字内容本身也要有包裹感:比如
p或div加border-bottom: 1px solid CanvasText,尤其在列表或段落密集区
color-scheme: dark 不等于高对比适配
color-scheme: dark 只影响浏览器 UI 和少数原生控件(如 input、button 默认样式),它既不触发 prefers-contrast,也不改变你写的任何自定义组件。用户开了系统高对比后,页面该灰还是灰,该黑还是黑,除非你真写了对应规则。
- 不要把
color-scheme: dark当作高对比开关,它只是告诉浏览器:“我支持深色模式”,仅此而已 - 验证是否真生效:Windows 开启“高对比主题”(设置 → 辅助功能 → 高对比度),Mac 开启“增加对比度”(系统设置 → 辅助功能 → 显示),然后刷新页面看是否所有文字、边框、焦点都清晰可辨
- 最容易被忽略的是:所有中间灰阶色(如
#6c757d、#adb5bd)在高对比下必须被替换,哪怕它们在深色模式里看着没问题
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











