本文最后更新于 2023年9月17日 上午
文章简介
现在,常见的操作系统,基本都已经适配了暗色/亮色
模式,并提供API接口:
macOS Mojave 10.14
开始提供了外观设置选项,支持设置 浅色 / 深色
外观。
Windows10 1809版
开始支持亮色/暗色
主题风格。
Android 10 (API 级别 29)
开始支持深色主题背景(第三方OEM厂商可能有所差异)。
iOS13
开始全面支持暗色模式。
那么,我们自己的网站如何适配暗色/亮色
模式呢?首先说一下最基础的媒体查询,然后带大家了解一下我的适配方案(纯JS
、CSS
的前端操作)。
本文章同步发布在:
媒体查询的优缺点
高版本的浏览器,可以提供媒体查询功能,我们使用CSS
,加入媒体判断即可:
1 2 3 4 5 6 7 8 9 10 11 12 13
| body { background: #fff; color: #222; }
@media (prefers-color-scheme: dark) { body { background-color: #222; color: #ddd; } }
|
prefers-color-scheme
支持三个值,分别是 :
no-preference
:无指定
light
:亮色
dark
:暗色
这样的好处是适配快,但是坏处也有,主要的体现是无法用户主动切换
:
举个例子,有些用户习惯把系统长期设置为暗色模式
,访问你网站时,想看清你网站的图片,希望调整成亮色模式,却必须到系统设置内,手动把系统配色调成亮色
再刷新网站,体验差
。
当时还好,我们有JS
,使用JS
也可以媒体查询,我们就不需要用CSS
来媒体查询系统暗色
或亮色
配色:1 2
| matchMedia('(prefers-color-scheme: dark)').matches
|
下文,我们开始给网站适配。
适配逻辑
本次适配的适配暗色/亮色
模式的用户操作逻辑分两种情况:存在暗色模式标识符
、不存在暗色模式标识符
。
暗色标识符
:由暗色/亮色
按钮调用的JS
实现存储在Cookies
或localStorage
内,用来提示JS
展现那种页面配色。
而暗色/亮色
的现实主要是,当需要给用户展现网站暗色
配色时,在HTML内<body>
标签内加入class="night"
。
不存在暗色模式标识符
用户进入网站,若之前没有手动点击网站上切换暗色/亮色
按钮(不存在暗色模式标识符),则使用媒体查询检测用户是否有开启暗色
模式,同步系统配色。
同时,媒体查询存在一定的兼容性问题,浏览器版本过低(如:IE 9),在查询失败时:
则逻辑判断用户当前系统时间,根据时间显示暗色
或亮色
配色。
存在暗色模式标识符
若用户之前有点击过切换暗色/亮色
按钮,查询Cookies
或localStorage
内暗色模式标识符来展示暗色/亮色
配色,优先级高于媒体查询
和时间判断
。
HTML结构
首先,我们依靠<body>
标签内是否存在class="night"
来实现,所以JS
引用,最好紧接<body>
标签:
1 2 3 4 5
| <body>
<i onclick="switchNightMode()" id="xm"></i>
<script type="text/javascript" src="/DarkMode/js/switch.js"></script>
|
1
| <i onclick="switchNightMode()" id="xm"></i>
|
这条是我实现的切换暗色/亮色
按钮,大家可以根据自己需要进行替换:
CSS结构
因为暗色模式的现实是依靠<body>
标签内是否存在class="night"
来实现:
1 2 3 4
| .night { color: #E0E0E0; background-color: #424242; }
|
派生
因为我们是直接在<body>
标签内添加CSS
叠加,所以,按照样式的优先级来说,在CSS
内使用,我们就需要使用派生方法写样式,如:
1 2 3 4 5 6 7 8 9 10
| .night span { color: #E0E0E0; }
.night .btn-danger { color: #fff; background-color: #e45353a1; border-color: #e45353a1; }
|
以此类推,根据自己亮色模式
下的样式,适配暗色模式
即可。
图片处理
另外,为了让暗色模式下,图片不要过度亮而刺眼,我们添加filter
样式:
1 2 3
| .night img{ filter: brightness(0.9); }
|
JS结构
JS
结构就比较复杂了,主要分三个部分
进入网站,判断是否启动暗色模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| function checkNightMode() { var Mode = document.cookie.split(";")[0].split("=")[1]; cookiesExp=new Date(new Date().setMonth(new Date().getMonth()+1)); if (Mode == 1) { $("body").addClass("night"); } else if (Mode == null || Mode == "undefined" || Mode == "") { if (matchMedia('(prefers-color-scheme: dark)').matches) { $("body").addClass("night"); } else if (matchMedia('(prefers-color-scheme: light)').matches) { $("body").removeClass("night"); } else if (new Date().getHours() >= 21 || new Date().getHours() < 7) { $("body").addClass("night"); } }
}
|
这个JS
是在用户进入网站,加载到<body>
标签时,进行判断,是否需要在表情内加入class="night"
。先判断是否有标识符,再判断媒体查询结果,最后判断用户系统时间,优先级逐级递减。
用户主动切换按钮
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function switchNightMode() { var Mode = document.cookie.split(";")[0].split("=")[1]; if (Mode == null || Mode == "undefined" || Mode == "") { if ($("body").hasClass("night")) { $("body").removeClass("night"); document.cookie = "DarkMode=0;path=/" + ";expires=" + cookiesExp.toGMTString(); $('#nightMode').removeClass("icon-yueliang").addClass("icon-zhishifufeiqiapianicon-"); } else { $("body").addClass("night"); document.cookie = "DarkMode=1;path=/" + ";expires=" + cookiesExp.toGMTString(); $('#nightMode').removeClass("icon-zhishifufeiqiapianicon-").addClass("icon-yueliang"); } } else if (Mode == '0') { $("body").addClass("night"); document.cookie = "DarkMode=1;path=/" + ";expires=" + cookiesExp.toGMTString(); $('#nightMode').removeClass("icon-zhishifufeiqiapianicon-").addClass("icon-yueliang"); } else { $("body").removeClass("night"); document.cookie = "DarkMode=0;path=/" + ";expires=" + cookiesExp.toGMTString(); $('#nightMode').removeClass("icon-yueliang").addClass("icon-zhishifufeiqiapianicon-"); } }
|
用户主动切换按钮,是在用户点击某个元素,触发onclick
函数事件,这边为还替换了页面id为nightMode
标签内的图标:
系统配色切换监听
其实,配置已经基本配置完成,但是如果用户设置的是自动变换配色,如Mac用户的外观自动
:
在系统自动切换暗色/亮色
的同时,如何让网站也一同切换?
答案就是创建监听器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| let lightMedia = window.matchMedia('(prefers-color-scheme: light)'); let darkMedia = window.matchMedia('(prefers-color-scheme: dark)'); let callback = (e) => { let prefersDarkMode = e.matches; if (prefersDarkMode) { checkNightMode(); } }; if (typeof darkMedia.addEventListener === 'function'||typeof lightMedia.addEventListener === 'function') { lightMedia.addEventListener('change', callback); darkMedia.addEventListener('change', callback); }
|
这样就可以在用户系统更改配色的同时,网站随之更改了。
Demo
最后,可以给搭建看看适配好的效果图网站:https://image.mintimate.cn
Tips
本次适配,标识符存储在Cookies
内,且设置切换一次后,有效期为30天,实际生产环境中,存储在localStorage
内可能是一个更好的选择。