大家有没有发现近些年的新设备从移动的到PC全都支持暗黑模式了?是的,这是个趋势。而且有些应用市场上不支持暗黑模式的应用都已经不能上线了。

暗黑模式本质上是一种换肤技术。早在20年前,qzone,人人网之类的应用就已经在web平台上实现了换肤。支持的皮肤可谓五花八门,还能商业化卖钱。所以暗黑模式不是什么新技术。然而为什么最近几年却出现新的活力?

主要原因在于操作系统层面都加入了对暗黑模式的支持。最开始是Android (Android 10),IOS (IOS 13)等移动端系统,之后桌面端也都加入了暗黑阵营。从而形成了从操作系统到应用程序,再到web的全链路黑化。用户能在操作系统上一键开启全局的暗黑模式。

暗黑模式香吗?

暗黑模式有什么优点,值的我们追随?

  • 视力保护:相信大家都有半夜刷手机的经历。晚上一个大白屏确实很刺眼。
  • 美学、用户习惯:这个角度完全是从个人的感觉出发。有人觉得白底黑字好看,有人觉得黑底白字更酷。另外还要考虑用户习惯,比如股票行情、命令行终端一般都是暗黑模式的。
  • 省电:移动设备屏幕电量消耗是个大头。因为屏幕的发光物理原理,深色比亮色就是消耗更少的电量。
  • 简单:暗黑模式是精简的换肤功能。如果做成五花八门的皮肤适配,工作量是巨大的。而暗黑模式配置就取了个平衡。大部分场景实现两套,一明一暗够用了。

快速开启

从2019年开始,web端来也黑化潮流。随着浏览器的更新升级,目前主流浏览器都是支持暗黑模式的。
简单开启暗黑模式只需要设置color-schemecss属性,页面即可根据当前系统设定,开启暗黑模式。

:root {
    color-scheme: light dark;
}

这里light dark表示网页同时支持暗色亮色。

color-scheme还有如下一些设置方法:light只支持亮色,dark只支持暗色,这样就完全忽略系统配置了。

需要注意的是,除了可以在系统开启暗黑模式,还可以在浏览器设置里开启。浏览器的设置优先级更高。

系统配置和浏览器配置

这个选项开启之后,我们的页面就整体黑化了。

color-scheme

但是这时候页面的颜色非黑即白,并不理想。
这是因为color-scheme其实只影响页面背景,默认文字,表单控件,滚动条等地方的颜色,且色值单一,作用很有限。
毕竟它是自动的嘛,做不到更详细的配置。这时我们需要使用另外一个规则优化配色。

优化配色

优化配色需要使用prefers-color-scheme规则。它是媒体查询(Media Query)的一部分。
我们把需要配色的CSS包裹在媒体查询中,让浏览器根据当前的色彩模式激活相应的样式。

/** 明亮模式生效的样式 */
@media (prefers-color-scheme: light) {
    :root {
        --fgcolor: #333;
        --bgcolor: beige;
    }
}
/** 暗黑模式生效的样式 */
@media (prefers-color-scheme: dark) {
    :root {
        --fgcolor: lightgreen;
        --bgcolor: #333;
    }
}
/** 页面使用上面两个变量 */
:root {
    color: var(--fgcolor);
    background-color: var(--bgcolor);
}

以上代码中,我们使用@media (prefers-color-scheme: dark) 为暗色模式配置了CSS变量,同样也配置了亮色的变量,并在页面根节点使用这些变量。这样浏览器会根据当前的颜色模式激活相应的变量。
现在我们的页面在暗色模式下颜色丰富了,好看多了。

prefers-color-scheme

需要注意的是:color-scheme 和 prefers-color-scheme没有必然联系。前者并不会影响prefers-color-scheme的判断。color-scheme可以更好的处理系统默认的一些样式,适合做一种兜底的方案,而prefers-color-scheme可以做更精细的控制。

js操作暗黑模式

刚才我们提到了媒体查询(Media Query)。它是web查询设备、环境信息的一个接口。通过这个接口,你不止可以获得兼容性情况,还可以知道当前主题设置情况。这包括一个CSS用法和一个js接口。

CSS用法刚才介绍过。这里着重介绍下js接口。

window.matchMedia方法返回一个媒体查询对象。对象里面有匹配的结果。我们也可以通过这个对象监听色彩模式修改。

// 获取查询结果
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
// darkModeMediaQuery.matches表示页面是否是暗黑模式
// 注册事件监听改变
darkModeMediaQuery.addEventListener('change', (e) => {
  const darkModeOn = e.matches;
  console.log(`Dark mode is ${darkModeOn ? '🌒 on' : '☀️ off'}.`);
});

页面加载背景色

SPA一般是比较大的,加载过程中一般会展示loader。而CSS加载因为是在html请求并获得数据之后,会有些滞后,并造成加载期间的白屏闪烁。
我们可以把支持的色彩主题放到html上,使加载页面最快的渲染出背景色。

<meta name="color-scheme" content="dark light">
<meta name="theme-color" media="(prefers-color-scheme: light)" content="white" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="black" />

这两条meta html标签就代表了之前的两条CSS规则。

暗黑模式切换

现在我们的页面支持了暗黑模式,用户假如不去修改设置是无感知的。
费了半天劲做了个隐藏功能,不划算啊。不如我们做个配置按钮,把这个功能放出来吧。让用户一点切换,而不用完全遵从系统配置。

这个需要配合CSS优先级来实现。

@media (prefers-color-scheme: light) {
  :root {
    --fgcolor: #333;
    --bgcolor: beige;
  }
}
@media (prefers-color-scheme: dark) {
  :root {
    --fgcolor: lightgreen;
    --bgcolor: #333;
  }
}
.light {
  --fgcolor: #333;
  --bgcolor: beige;
}
.dark {
  --fgcolor: lightgreen;
  --bgcolor: #333;
}

我们在之前的prefers-color-scheme样式后面加上light, dark两个class。他们的优先级更高。我们在body上加上其中任意一个class时候就能覆盖prefers-color-scheme的规则。prefers-color-scheme用作跟随系统配置的基础规则。

有了暗黑模式手动切换的两个class,我们页面的配色不一定需要跟系统保持统一了。我们甚至还可以把部分页面赋予相反的class,切换为不同的色彩模式。

图片资源切换

有些静态资源,如图片具有明显的明暗属性,在不同色彩模式下也是有必要做切换的。
我们可以使用picture元素的媒体查询语句给不通的色彩主题替换不同的图片。

<picture>
    <source srcset="/images/dark.jpg"
            media="(prefers-color-scheme: dark)">
    <img src="/images/light.jpg">
</picture>

source为同一个图片标签指定了多个src来源。浏览器根据当前设置启用不用的src。

图片资源切换之filter

以上方法,需要设计师为不同的主题提供不同的图片。设计表示🍐很大。我们不妨偷懒下,做一种简单点的图片资源的切换方法吧。

.dark svg {
  filter: invert();
}

这个css使用invert filter反色。效果是这样的。

反色

嘿白图上看样子还不错。

图片资源切换之fill

svg还可以通过fill,stroke属性直接修改颜色。

.dark svg {
    fill: buttontext;
}

这里通过修改矢量图形的fill color直接修改配置暗黑色彩。
buttontext是一个内置色彩变量,它跟当前前景色是相反的。下面有详细介绍。

强调色(accent-color)

强调色也是近年新增的一个从系统传过来的颜色配置。浏览器默认用系统的强调色渲染内置的表单组件。
系统强调色配置

你可以用这个CSSaccent-color属性修改默认的强调色。

form {
    accent-color: green;
}

另外你也可以把强调色当成一个特殊的颜色使用。

div {
    background-color: accentcolor;`
}

这是一个跟随系统强调色而变化的div。注意accent和color中间没有-符号。

这个颜色好使,还有类似的色值么?
确实有一堆呢,在CSS标准里!

总结

以上就是所有色彩模式切换的内容了。我们讲到了color-schemeprefers-color-scheme等基础配置和一些小技巧。接下来,实践起来,让我们一起黑化web世界吧。