暗黑模式是系统级别的
所谓的暗黑模式并不是现在才有的,这个事实已经存在很久了。如果你很早就接触过电脑的话,你可能会发现你使用过的电脑屏幕经历过好几个过程,看起来会像下面这样:
是不是觉得既熟悉又陌生。既然如此,为什么今年会成为设计或者说 Web 端的一个热点呢? 其实这一切都应该归功于 Apple 公司,在 macOS 系统中提出了 dark 和 light 两种视觉模式,即 暗色(dark) 和 高亮(light) 两种皮肤,而且这两种皮肤是系统级别的,我们可以通过系统上的切换,让整个电脑上只要支持 dark/light 模式的应用都可以轻易切换。
那么为什么要从系统级别去做这个事情呢?这是有原因的。系统面对的用户群体中朋部分人士在身体上存有一定的缺陷,比如说色盲的用户群体。也就是说,这种暗黑模式或者高亮模式对于有色盲的用户群体是非常友好的。既然如此,为了让自己的 Web 网站或者 Web 应用能向系统级别靠齐,就有了网站级别的暗黑模式。 你可能在很多网站的右上角看到了一个提供暗黑和高亮模式的切换按钮。
暗黑模式实现原理
给 Web 网站或者 Web 应用添加暗黑模式的基本原理我想大家应该很清楚,事实上也非常的简单。
正如上图所示,给同一个 Web 网站或 Web 应用提供多套皮肤,用户根据自己的喜欢进行选择。那么给网站添加暗黑模式是同一个原理,就是给网站同时提供两套皮肤,即 theme1.css 和 theme2.css。
早期我们可能会借助于 JavaScript 脚本,根据用户的选择在一个<link />标签上进行两个主题文件(即.css 文件)切换来实现:
这可能是一种比较古老的实现方案。也是大家最为熟悉的方案。
CSS 实现暗黑模式切换
时至今日,给 Web 网站或 Web 应用程序实现暗黑模式已有多种模式。可以是纯 CSS 的方式,也可以是 CSS 和 JavaScript 结合的模式。那么接下来,我们来看看具体的实现方式。
媒体查询 prefers-color-scheme
CSS 有一个特别强大的特性,那就是媒体查询 @media,CSS 的 @media 规则可以用于有条件地将样式应用于文档以及其他各种上下文和语言,如 HTML 和 JavaScript。在 W3C 的Media Queries Level 5引入了 “用户首选媒体特性”,即 Web 网站或应用程序检测用户显示内容的首先方式的方法。
比如 prefers-reduced-motion 这个媒体查询就可以检测页面上的动画,假设设备开启了“Reduce motion”选项,就可以通过该媒体查询选项让页面上的元素是否具有动效:
如果用户开启减少动效的喜好,那么就不要在元素上使用动效:
如果用户没有在系统级别设置该选项的话,可以像下面这样让按钮有动效:
Web 上的其他具有动效的元素都可以像上面之样使用, 上面只是用 button 为例。
如果 Web 网站有很多元素具有动效的话,还可以将所有与动效相关的 CSS 放在一个独立的文件中,然后通过 link 的 media 属性来加载:
为了说明 JavaScript 如何控制 preferences-reduced-motion。这里假设你在项目中使用了 Web Animation API。当用户开启了偏好设置,CSS 规则会被浏览器动态触发,这样一来我五一需要自己监听变化,然后手动停止与动画相关的东西:
如果你有强迫症,强迫减少网站上所有有动效停下来,还可以像下面这样的简单粗暴的操作:
有关于 prefers-reduced-motion 更详细的介绍可以阅读 @Thomas Steiner 的博文“ Move Ya! Or maybe, don’t, if the user prefers-reduced-motion!”。
似乎上面的内容偏离了我们今天要聊的主题,大家不用着急。只不过是拿 prefers-reduced-motion 这个媒体查询来抛砖引玉而以。在 CSS 中通过媒体查询的 prefers-color-scheme 特性和 prefers-reduced-motion 类似,不同是,该特性是用于检测用户是否要求页面使用 light 还是 dark 主题。该媒体查询常见的值有:
no-preference:表示用户未指定操作系统主题。其作为 布尔值 时以 false 输出
light:表示用户的操作系统是浅色主题(light)
dark:表示用户的操作系统是深色主题(dark)
也就是说,通过 prefers-color-scheme 媒体查询要让暗黑模式(dark)开启深色系主题,可以像下面这样使用:
当然在非 dark 模式下,你的样式可能像下面这样:
注意上面提供的示例代码仅仅是最基本的颜色配置方案,但也可以说完成了近 90%的工作。但细节决定成败。如果要让你的 Web 网站或应用程序在 light 和 dark 模式切换下能有较好的效果,还需要注意其他的一些细节,比如说 img、svg 等元素的细节处理。有关于细节方面的,稍后我们会再讨论,暂且不表。
正如上面的示例所示,我们是通过 CSS 的媒体查询特性来检测 dark 模式,即通过检查媒体查询是否首选。那么颜色方案是否匹配还需要检查当前浏览器是否支持 dark 模式。
我们可以像下面这样来检测浏览器是否支持 dark 模式:
至于哪些浏览已支持prefers-color-scheme特性,我们可以通过 Caniuse 来查询:
前面的示例简单的向大家演示了如何给 Web 网站或应用程序设置暗黑模式。但有很多细节我们需要去注意。
在一个应用中只 dark(暗色系)和 light(亮色系)只能是二选一,永远不可能两者共存。为什么要提这个呢?我们从加载策略来做衡量。如果我们不管三七二十一,直接将所有样式(普通样式、亮色系样式和暗色系样式)都用一个.css 文件加载的话会强迫用户在关键的渲染路径中下载 CSS(包括你不想要的模式代码也加载进来了)。为此,为了优化加载速度和给用户提供更好的体验,我们可以将 CSS 分成三个部分,以延迟非关键的 CSS:
style.css:网站上普通样式(通用样式)
dark.css:暗色系所需样式规则
light.css:亮色系所需样式规则
其中 dark.css 和 light.css 可以通过<link media="" />有条件的加载。加上并不是所有浏览器都已支持 prefers-color-scheme 特性,所以我们在加载通用样式 style.css 规则的基础上动态默认加载 light.css。即,不支持该特性的浏览器会按下面的顺序加载 CSS:style.css ➜ light.css ➜ dark.css;如果支持该特性的浏览器则会按下面的顺序加载 CSS:style.css ➜ dark.css ➜ light.css。具体的代码如下:
按照该规则,前面的 CSS 示例代码,我们就可以按下面这样的文件来划分:
这里使用了 CSS 自定义属性,该示例再次向大家演示了 CSS 自定义属性的强大之处。
CSS 的新特性 color-scheme
CSS Color Adjustment Module Level 1提供了另一个新属性 color-scheme。该特性会告诉浏览器该应用的颜色主题和允许用户代理的特殊变体样式表,而且它还可以让 Web 中的部分区域的渲染在 dark 和 light 之间切换,比如让浏览器渲染渲染的表单域是个黑色背景和高亮文本。
来看一个简单的示例代码:
同样的,还可以 HTML 的 meta 标签来设置:
到目前为止,支持 color-scheme 的浏览器还较少:
俗话说,百闻不如一见,这里向大家展示一个由 @Thomas Steiner 提供的案例:
上面这个案例和以往提供的案例有所不同。该案例按前面所讲的分成三个独立的样式文件:style.css、dark.css 和 light.css。尝试切换暗黑模式并重新加载页面,你会发现不匹配的样式文件仍然会被加载,只是优先级有所差异,这样做它们就不会与站点当前所需的资源竞争。
当网站是在 light 模式下,样式文件加载优先级是 style.css ➜ light.css ➜ dark.css,即 dark.css 权重最低(Lowest):
当网站在 dark 模式下,样式文件加载优先级是 style.css ➜ dark.css ➜ light.css,即 light.css 权重最低(Lowest):
当浏览器不支持 prefers-color-scheme 而且设置 light 为默认模式,那么样式加载优先级会和高亮模式一样:
特别声明,上面三图截图来自于“Hello darkness, my old friend”一文。
上面示例还做了另一个细节上的优化。和其他媒体查询更改一样,可以通过 JavaScript 的订阅来更改暗黑模式。比如可以动态更改页面的 favicon 或更改<meta name=“theme-color”>来决定 Chrome 中 URL 栏的颜色。代码并不复杂:
CSS 混合模式来助攻
上面我们看到的都是原生 CSS 处理暗黑模式的技术方案。事实上我们还可以通过 CSS Hack 来实现,采用 CSS 的 filter 和 CSS 的混合模式 mix-blend-mode。
下面这个示例就是 CSSfilter 实现的暗黑模式:
Switch from light to dark mode using the toggle [CSS filter]@airenCodePen
关键代码很少:
另外还可以使用 CSS 的 mix-blend-mode 来实现:
如果你想让页面中部分元素忽略 mix-blend-mode:difference 带来的影响,可以使用 isolation: isolate:
效果会类似下图这样:
有关于这方面的详细介绍可以阅读 @thoughtspile 的 How to create a dark theme without breaking things: learning with the Yandex Mail team 和的 @wgao19 Night Mode with Mix Blend Mode: Difference。
其他细节
根据前面的内容去操作,不管是使用 CSS 的媒体查询 prefers-color-scheme、新特性 color-scheme 还是借助 CSS 的滤镜 filter 或混合模式 mix-blend-mode 都可以轻易的给 Web 网站或应用程序添加暗黑模式。
注意,很多同学有一个小误区,认为 filter 和 mix-blend-mode 只能用于图片,事实上并非如此,他可以运用于 Web 的各种元素上。
那么掌握了实现暗黑模式的技术方案就能做出好的效果吗?并非如此,其中还是有很多细节需要我们注意。比如颜色的配置、Web 媒体(图片、Icon 等)和可访问性等方面的处理都值得我们去推敲。
颜色的配置
在给 Web 网站或 Web 应用设计暗黑模式的时候,你千万不要钻到死胡同里。暗黒模式并不仅仅是 黑(black) 与 白(white) 之间的切换。你想像一下,在一个深夜密不透光的地方,用你的肉眼注视着一块高亮的屏幕,时间久了,你会有什么样的一个感觉:
正确的做法是应该为你的品牌色系提供一个暗色系版本,如果不奏效的话,可以根据需要在黑色和灰色之间选择一个平均颜色。比如说,Web 的背景颜色是 black(#000)(或者接近 #000)的话,建议你前景色(比如文本颜色)取值为 rgb(250,250,250)(或者靠近这个颜色值)。这样才能让你的整体效果不至于亮瞎用户的眼睛。比如下面这样的一个效果:
如果你实在拿不准配色是否合理(Web 安全颜色),你可以借助在线工具,比如 Contrast Checker :
该工具是根据 WCAG 2.0 guidelines for contrast accessibility 标准来做的。比如下面图所展示的效果就是一个较好的效果:
除了借助工具来检测之外,浏览器的插件也是把利器,可以借助浏览器有关于 Accessibility 相关的插件来做检测,就比如小站,检测出来的结果令人汗颜:
事实上,很多网站都存在这样的缺陷:
图片处理
在暗黑模式下,图片的处理也是非常重要的。它们可能会直接影响用户的体验,太亮的图像可能会让用户感到困惑和不舒服。而且有人做过这方面相应的调查,大多数被调查的人在暗黑模式下更喜欢亮度低的图像。比如下面这张图:
左侧是暗黑模式下效果,右侧是在高亮模式下效果。
为了能更用户更好的体验,这里提倡在不同模式下给用户展示不同效果的图像,但并非说在不同的模式下引入不同的图片。就目前 CSS 的技术,我们在同一图像源下可以很好的对图像做处理。比如,粗暴一点的使用 CSS 的 opacity,温柔一点的使用 CSS 的混合模式 mix-blend-mode(如果是背景的话则用 background-blend-mode)或 filter。
不过这里有一个细节需要注意,我们引入的图像源有可能是.svg 的矢量图,如果希望给矢量图(更多是 Icon)一个不同于位图(更多是图像,照片)的重新着色处理。那么我们可以通过属性选择器和伪类选择器将.svg 过滤掉:
如果你希望给用户更多的选择的话,我们可以将 CSS 自定义属性和 JavaScript 结合起来,可以让用户根据自己的喜好去做调节。这里还是拿图片的处理为例吧:
也可以参考下面这个Demo,使用 filter 修改图片效果:
CSS Filters@nitnelavCodePen
当然,如果你是位追求极致的同学,希望在暗黑模下给用户提供最好的图像,而不是随便修改图片的亮度或饱和度;但又不希望因加载图片资源过多而影响整体的性能(甚至不希望因为自己的原因过渡浪费流量)。如果真的是这样的话,你可以由设计师为暗黑模式下提供特定的图片,然后与<picture>元素一起使用。可以在<picture>的<source>根据媒体属性的设置加载所需要的图片资源:
暗黑模式下会加载 dark.webp 图片,在高亮模式或者不支持 prefers-color-scheme 的浏览器中会加载 light.webp 图片,不支持<picture>的浏览器会加载 light.png 图片。你将看到的效果可能如下:
图标的处理
刚才提到过,很有可能你 Web 网站或 Web 应用程序中有很多 Icon 图标用的是 SVG 图标。在暗黑模式下,同样要对 Icon 图标做相应的处理。这里来看两种情景。
先来看第一种,那就是.svg 文件和其他格式的图像一相通过<img>标签引入。由于该 Icon 很有可能是纯色的,因此在暗黑模式下,我们可以通过 filter 来做 dark/light 之间的切换:
如果你还想调整成其他的颜色,还可以像下面这个 Demo 来操作,增加 filter 的属性值选项:
Change Icon Color with CSS Filter (Forked @ Cassie Evans)@airenCodePen
该方法和处理图像的方法是类似的。接下来我们再来看第二种方式。使用的 Icon 图标很有可能是内联的 SVG,针对这样的场景,我们可以使用 CSS 的 currentColor 属性。currentColor 最大的特性就是可以根据 color 的值来决定元素的颜色,而对于 SVG 绘制的 Icon 图标,主要由 path、circle、rect 这样的元素构成,这些元素可以通过 fill、stroke 来决定填充色和描边色。换句话说,我们在使用内联 SVG 时,将 SVG 中用到 fill 和 stroke 的属性值都强制设置成 currentColor,就像下面这样:
另外在媒体查询中设置:
如果你分成多个文件的话,可能会像下面这样的:
让切换有一个过渡效果
熟悉 CSS 的同学都应该记得,CSS 的 transition 可以让元素在两个状态的切换过程中有一个平滑过渡的效果,以至于不会那么生硬:
而我们聊的 dark/light 两模式之间的切换刚好稳合 transition。加上 dark/light 两模式之间的切换就是 color 和 background-color 属性值的切换。为了让整个切换过程有一个过渡效果,我们可以把 transition 加上来。比如:
JavaScript 实现 dark/light 模式切换
如果你不依任 CSS,或者说希望让自己的 Web 网站或 Web 应用程序都具备 dark/light 模式的切换,那么可以通过 JavaScript 来实现。因为 dark/light 模式的切换说到底就是两套主题的切换。当然,你可以让该 JS 的能力更为强大一些,不仅仅是对.css 文件的切换,粗暴简单的实现网站换肤这样的一个功能。或许你可以这样做:
在网站上提供相应的切换按钮(比如一个 tab 选项卡,也可以是一个 radio 按钮),方便用户自行选择
该 JS 可以对系统级别做监听,如果用户从系统级别开启了暗黑模式,那么就把样式文件切换到 dark.css 下
还可以根据时间来做一个 dark/light 模式的切换,比如说白天采用 light 模式,晚上使用 dark 模式
和 CSS 实现 dark/light 模式切换一样,还可以在 JS 中加上 transition 效果,让模式在切换的过程有一个过渡效果
为了节约篇幅,这里就不把 JavaScript 代码贴出来了,感兴趣的话可以看看 @Koos Looijesteijn 的 A guide to implementing dark modes on websites 一文。文章中详细的根据上面几个过程,向大家展示了对应的 JavaScript 代码。如果你不愿阅读文章的话,可以点击链接直接阅读源代码。
不过就我个人而言,我不太推荐使用 JavaScript 方案,这可能和我自己的信条有关系:
在 Web 端能用 CSS 实现的绝不借助 JavaScript。
浏览器配置
下面有一个简单的示例:
Switch light / dark mode with prefers-color-scheme: dark@airenCodePen
在该示例的页面上没有提供任何切换按钮给用户做选择。主要目的是希望页面能根据系统级别的设置来决定采用什么主题。假设你的系统默认就开启了暗黑模式。但你在浏览器看到的效果也不一定是 dark 模式下的效果。这主要是因为浏览器对 prefers-color-scheme 支持有一定的差异。不过我们可以对浏览器做一些设置,让页面能正常的跟着系统设置做出正确的渲染。
如果你使用 Firefox,可以在地址栏中输入 about:config,然后鼠标右键点击选择“新建(New)” → “整数(Integer)”,新建整数 ui.systemUsesDarkTheme,并且将其值设置为 1:
如果你使用的是 Safari 浏览器,可以使用它自带的工具来查看效果:
最终在支持的浏览器下看到的效果如下:
除此之外,还可以给浏览器安装插件。比如 @CHRIS HOFFMAN 在他的 How to Enable Dark Mode for Google Chrome 文章中就详细的介绍了怎么在 Chrome 浏览器安装Dark Reader 插件,让 Web 页面具有暗黑模式浏览效果:
小结
上面我们通过不同的方式向大家阐述和演示了如何实现黑暗模式和高亮模式切换的解决方案。有粗暴简单的方式,原始的切换样式表的方式,还有采用一些新的 CSS 特性,比如 CSS 自定义属性,新的媒体查询特性,还有神奇的滤镜和混合模式。而且这些解决方案中既有 CSS 和 JavaScript 的混合解决方案,也有纯 CSS 的解决方案,甚至还有原生系统和浏览器通信的解决方案。还是那句老话,不管哪种解决方案或者技术手段,都有自己的利弊,没有最好,只有最适合的使用场景。在实际使用的时候,应该具体问题具体分析。
本文转载自公众号淘系技术(ID:AlibabaMTT)。
原文链接:
https://mp.weixin.qq.com/s/dxHAlvnMNxI9r0MXruZ8pA
评论