先记住这个答案
prefers-color-scheme 反映用户或系统声明的浅色、深色偏好,页面可以据此选择默认配色。若用户在站内手动选过主题,应用还要定义覆盖顺序并保存选择,否则系统变化会把用户的决定冲掉。主题不只是换背景,还要检查文字、边框、图标和原生控件的对比度。
- 先写默认可读配色
- 深色偏好可覆盖语义变量
- 手动主题应覆盖系统偏好
color-scheme不是完整主题
文档站系统主题与手动选择
文档站默认浅色,用户系统切深色后,希望背景、文字和边框一起变化。把颜色集中为 CSS 自定义属性,深色媒体查询里覆盖变量,组件继续使用同一套语义变量。若用户又在站内明确点了“浅色”,应通过根元素的主题标记覆盖系统值,而不是下一次系统变化就把他的选择冲掉。图片、代码高亮和表单控件也要检查对比度。
:root { --surface: white; --text: #161616; }
@media (prefers-color-scheme: dark) {
:root { --surface: #171717; --text: #f5f5f5; }
}
:root[data-theme="light"] { --surface: white; --text: #161616; }
body { background: var(--surface); color: var(--text); }媒体查询匹配时,深色变量声明进入级联,最终仍要按选择器优先级和规则顺序决定值。 示例的深色媒体查询和显式浅色选择可能同时命中。它们的选择器权重与源码顺序决定最终变量值,因此手动覆盖规则要放在合适位置,不能只在 JavaScript 里记住一个布尔值却没有对应的 CSS。若还支持用户手动深色,也应给显式深色写对称规则;否则系统恢复浅色后,界面可能与设置面板的选项不一致。
系统偏好仍参与 CSS 级联
单纯在后面写一个同等权重的浅色覆盖,只能处理显式浅色,显式深色还需要对应规则;生产方案通常把系统模式与手动模式区分为状态,再明确每个状态的变量来源。原生表单控件和滚动条可以通过 color-scheme 告知浏览器支持的配色,但它不会自动替你设计所有页面颜色。
实现手动主题时,可用根元素的显式类或数据属性覆盖系统默认变量。首次渲染要尽早读取用户保存的选择,否则页面可能先闪白再切黑;系统偏好变化只应影响尚未手动选主题的人。表单输入、代码高亮和图片背景也要检查,否则页面主体虽变黑,局部仍会出现刺眼白块。
容易答错的地方
- 把系统偏好当成用户的站内选择
- 系统值可变,手动选择应单独记录并明确覆盖优先级。
- 只改背景不改文字和控件
- 主题要覆盖语义颜色、图片、焦点样式与原生控件的可读性。
面试官还会怎么问?
页面第一次加载闪一下浅色怎么办?
尽早确定已保存的手动主题,并让首屏 HTML 的主题标记与最终 CSS 一致。
color-scheme 等于完整深色主题吗?
不是。它帮助浏览器选择原生控件等的配色,页面自定义颜色仍要自己定义。
用户选了浅色但系统是深色,应听谁的?
若站内提供显式选择,通常让用户手动设置覆盖系统偏好,并提供回到“跟随系统”的选项。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。