必须使用@media (forced-colors: active)而非prefers-contrast,因其是系统接管颜色的唯一信号;CanvasText等语义色不可加引号;变量须带可访问回退值;该媒体查询必须置于最后且不可合并。

必须用 @media (forced-colors: active),不是 @media (prefers-contrast: high)
前者是系统真正接管颜色渲染的唯一信号,后者只是“用户偏好”,浏览器不强制执行,实测中 90% 的高对比失效都源于误用它。Windows 高对比主题、macOS “增加对比度”开启后,只有 @media (forced-colors: active) 能触发浏览器剥离所有自定义 color/background-color,并强制使用系统语义色。漏括号、写成 @media prefers-contrast: high(缺括号)、或在未开启系统级高对比时调试,都会让规则静默失效。
CanvasText 和 Canvas 是关键字,不是字符串,不能加引号
在 @media (forced-colors: active) 块里写 --color-text: "CanvasText" 或 --bg: 'Canvas' 直接导致变量无效——引号会让 CSS 解析为字符串字面量,而系统语义色必须是无引号的保留关键字。它们和 currentColor 一样,是 CSS 原生识别的标识符。
- 正确:
--text-primary: CanvasText;、--bg-surface: Canvas; - 错误:
--text-primary: "CanvasText";、--bg-surface: CanvasText;(拼错)、--bg-surface: #000;(十六进制值会被系统忽略) - 常见系统语义色:
LinkText、ButtonFace、Highlight、GrayText,全部不加引号
变量声明必须带回退值,且 @media (forced-colors: active) 必须放在最后
如果只写 color: var(--text-primary);,而 --text-primary 在 forced-colors 模式下未被重新定义,就会退到初始值(可能是透明或不可读的浅灰),UI 崩溃。每个 var() 都要显式提供回退:
-
color: var(--text-primary, #333);—— 回退值必须是合法 CSS 颜色,且满足 WCAG AA(≥4.5:1) -
@media (forced-colors: active)规则块必须置于所有其他主题媒体查询(如@media (prefers-color-scheme: dark))之后,否则会被前面的:root定义覆盖 - 不要合并写法:
@media (prefers-color-scheme: dark) and (forced-colors: active)不被支持,必须拆成两个独立块
Grid 布局本身不影响适配,但容易导致 forced-colors 覆盖不全
Grid 只管排列,不参与颜色逻辑。问题出在实践:你可能在多个选择器里分散设置了 color 或 background-color(比如 .card-header、.grid-item:nth-child(2)),结果只在 :root 里重定义了变量,却忘了给这些具体选择器也加 @media (forced-colors: active) 下的兜底样式。
立即学习“前端免费学习笔记(深入)”;
- 推荐做法:把所有颜色控制收敛到 CSS 变量,再统一在
@media (forced-colors: active)中重置:root变量 - 但若某处用了硬编码色值(如
button { background: #007bff; }),就必须单独补一条button { background-color: ButtonFace; }在 forced-colors 块里 - 特别注意伪类状态:
a:hover、input:focus等,它们的颜色同样会被剥离,需显式用LinkText、Highlight等重写
box-shadow、渐变、rgba() 和任何非系统语义色,这不是 bug,是规范行为。真正难的不是写对媒体查询,而是提前预判哪些视觉线索(阴影、圆角、微妙的背景差)会在 forced-colors 下消失,并用语义色+结构化标记(如 aria-current、role="tab")补足可感知性。


















