🌻 前言
网站切换主题色已经是非常常见的功能了,提供浅色和暗色两种色调可以满足用户的使用习惯,帮助这些用户获得更好的访问体验。但是只能用户手动切换主题。
那如果用户已经将系统切换到了深色模式,当他们打开我们网站的时候,网站自动切换到深色模式,第一感觉就不会那么扎眼,是不是会有更好的第一印象,提高留存。
好了,铺垫到位了哈哈,引出本文将要讲的内容:实现网站跟随系统主题变色。
🎨 实现
实现的基础知识
css中有一个属性color-scheme
,可以设置元素支持的系统配色方案,有以下四个值:
- normal:使用浏览器的默认配色方案进行渲染;
- light:使某元素支持使用操作系统浅色方案来渲染元素;
- dark:使某元素支持使用操作系统深色配色方案进行渲染;
- only:禁止用户代理覆盖元素的配色方案,必须搭配light或dark使用,例如
only dark
关键词表示禁用浅色模式,只应用深色模式;
简单来说,这个属性就是设置元素支持的系统配色方案,例如仅支持系统深色模式,注意是设置支持的值,而不是直接设置配色方案,他是会影响表单控件和滚动条等的颜色的,你可以简单在下面码上掘金上体验下效果:
jcode
与此相关的,有个媒体查询@media (prefers-color-scheme: light|dark)
。它用于检测操作系统是浅色还是深色主题,从而设置针对不同的配色方案设置不同的样式。
很容易理解,例如下面代码:
:root{
--bg-color: #f2f2f2;
}
div{
background: var(--bg-color);
}
@media (prefers-color-scheme: light) {
:root{
--bg-color: #f2f2f2;
}
}
@media (prefers-color-scheme: dark) {
:root{
--bg-color: #000;
}
}
一个div的背景色由css变量--bg-color
设定,那么我们就可以通过在@media prefers-color-scheme
媒体查询中修改这个变量的值来修改页面样式。
由此,想必你应该想到了,那利用这个媒体查询不就能实现根据系统主题切换网页配色了吗?确实可以,但,未免有些粗糙!
问题
1.这样做切换网页配色,那网页只能跟随系统主题来变化了,但是用户对于系统主题色,一般都是设置一种之后基本就不会再变了,所以这样来实现切换网站主题色岂不是相当于多写了一套“闲置无用”的主题方案。
2.常规实现网站手动切换主题色有很多种方案,例如在根元素设置[theme="dark"]
属性,通过切换其属性值来切换网站主题,而网站中的各种配色都以CSS变量形式存储在theme
的属性选择器下。
这样的话属性选择器中的css变量和@media prefers-color-scheme
中的css变量就会重合,就会根据书写顺序进行覆盖,那只能有一种切换网站主题的方案生效了。
而网站随系统变色,本身就属于附加功能,它和用户自由切换主题色相比,如果为了网站随系统变色而使手动切换主题色失效,那岂不是本末倒置了。
所以怎样才能使手动切换网站配色和随系统变色公共生效呢?
答案很简单,在网站配置了手动切换配色的功能后,只需要在网站加载时,通过 JS 查询系统主题,然后在代码中切换到对应的配色不就好了吗😎
关键代码
JS 中有一个 API:window.matchMedia()
,它可以检测是否匹配到了某个媒体查询。
问题简单起来了,只需要利用window.matchMedia()
检测@media prefers-color-scheme
媒体查询,即可根据系统主题切换不同的网站配色了。代码大致如下:
// html
<div id="app" theme="light"></div>
// css
[theme="light"]{
--bg: #f2f2f2;
}
[theme="dark"]{
--bg: #000;
}
#app{
background: var(--bg);
}
// js
const app = document.getElementById('app')
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// 切换到深色主题
app.setAttribute('theme','dark')
} else {
// 切换到浅色主题
app.setAttribute('theme','light')
}
效果如下:
jcode
甚至你还可以监听颜色方案的变化,以便在用户改变系统颜色主题时跟随切换网站配色:
window.matchMedia('(prefers-color-scheme: dark)').addListener((e) => {
if (e.matches) {
app.setAttribute('theme','dark')
} else {
app.setAttribute('theme','light')
}
});
又一些问题
在上一章讲color-scheme
属性时,想必就有人想到了,是不是可以通过切换color-scheme: light
、color-scheme: dark
,配合@media prefers-color-scheme
媒体查询写两套样式(浅色和深色的各写一套),然后也能实现手动切换网站主题?
但事实上并不能,因为color-scheme
本质上只是设置支持的系统主题,而不是设置系统主题的,所以并不会影响@media prefers-color-scheme
的匹配。说人话就是,即使你设置了color-scheme: only dark
,但你的系统是浅色模式,@media (prefers-color-scheme:dark)
里的代码也不会生效。
那color-scheme
有什么用呢?其实吧,这玩意确实没什么用,但是如果你的网站使用了很多系统控件,就可以用color-scheme
来控制它们在系统深色模式下的颜色。
系统控件在浅色和深色模式下是有两套默认的配色的。你可以使用light-dark()
函数分别设置它们在浅色和深色模式下的样式。
light-dark()
函数用于检测是否设置了浅色或深色配色方案,使用方式很简单,用逗号分隔浅色和深色模式的值即可,如下:
background: light-dark(blue,red); //blue就是浅色模式下的值,red是深色模式的值
无论是手动切换系统主题,还是设置 color-scheme: only dark ,其实都可以影响到 light-dark()。但往往我们都做法是,全部设置color-scheme: light dark
,然后利用light-dark()
函数设置系统系统控件的样式。
注意: 我一直在说系统控件,不代表 light-dark() 只能作用于系统原生控件,只是它用来设置普通网页元素的配色方案的场景不多。
关于深色模式
深色模式可不只是切换一些配色这么简单,里面的学问也很多,例如为了确保可访问性和可读性,要注意选择颜色和调整一些板式:
深色模式的颜色不是简单的反转,例如白色背景变为黑色,黑色文本变为白色。显然,全黑背景上的纯白色文本使得阅读长段文本会导致眼睛疲劳。深色模式一般最好使用低对比度的颜色,例如使用稍微灰白色的文本和/或深灰色背景。降低对比度可以提高可读性,Lea Verou 的对比度计算器 这个工具可以计算颜色对比度,优化配色方案。
深色模式设计不应仅限于选择深色。还应该考虑改变排版样式,以保持深色模式可读性。例如深色模式下的浅色文本对比度会相对较高,可以考虑提高文本行高或字距离。
兼容性
兼容性方面,其实主要只需要考虑 prefers-color-scheme
的兼容性就好,还凑合,color-scheme
也差不多,light-dark()
相对兼容性较差,但是一般使用场景比较少。
🎁 最后
学如逆水行舟,不进则退~👊👊👊
先看后赞,养成习惯👍
收藏吃灰,不如学会🍗
点个关注,不要迷路🪤